Bug 1586471 - Part 3. Allow to drop data from external application on GeckoView. r=dom-core,geckoview-reviewers,amejiamarmol,edgar

The dropped item is set on drop event handler in Android. Current
Gecko's implementation caches dropped item as `DataTransfer` when any
drag events are fired. It will works on desktop platform, but doesn't
on GeckoView. Even if drop item is set by handling drop event,
`DataTransfer`'s cache isn't refreshed.

So if drop event is received, we re-fill DataTransfer on GeckoView.

Differential Revision: https://phabricator.services.mozilla.com/D197331
This commit is contained in:
Makoto Kato 2024-01-28 07:49:53 +00:00
Родитель d88b0348e7
Коммит 30d76ab208
10 изменённых файлов: 133 добавлений и 59 удалений

Просмотреть файл

@ -1720,7 +1720,8 @@ void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
}
case eDragEventClass: {
RefPtr<BrowserParent> browserParent = remote;
browserParent->Manager()->MaybeInvokeDragSession(browserParent);
browserParent->Manager()->MaybeInvokeDragSession(browserParent,
aEvent->mMessage);
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;

Просмотреть файл

@ -3152,13 +3152,53 @@ mozilla::ipc::IPCResult ContentChild::RecvPWebBrowserPersistDocumentConstructor(
return IPC_OK();
}
static already_AddRefed<DataTransfer> ConvertToDataTransfer(
nsTArray<IPCTransferableData>&& aTransferables, EventMessage aMessage) {
// Check if we are receiving any file objects. If we are we will want
// to hide any of the other objects coming in from content.
bool hasFiles = false;
for (uint32_t i = 0; i < aTransferables.Length() && !hasFiles; ++i) {
auto& items = aTransferables[i].items();
for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) {
if (items[j].data().type() ==
IPCTransferableDataType::TIPCTransferableDataBlob) {
hasFiles = true;
}
}
}
// Add the entries from the IPC to the new DataTransfer
RefPtr<DataTransfer> dataTransfer =
new DataTransfer(nullptr, aMessage, false, -1);
for (uint32_t i = 0; i < aTransferables.Length(); ++i) {
auto& items = aTransferables[i].items();
for (uint32_t j = 0; j < items.Length(); ++j) {
const IPCTransferableDataItem& item = items[j];
RefPtr<nsVariantCC> variant = new nsVariantCC();
nsresult rv =
nsContentUtils::IPCTransferableDataItemToVariant(item, variant);
if (NS_FAILED(rv)) {
continue;
}
// We should hide this data from content if we have a file, and we
// aren't a file.
bool hidden =
hasFiles && item.data().type() !=
IPCTransferableDataType::TIPCTransferableDataBlob;
dataTransfer->SetDataWithPrincipalFromOtherProcess(
NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
nsContentUtils::GetSystemPrincipal(), hidden);
}
}
return dataTransfer.forget();
}
mozilla::ipc::IPCResult ContentChild::RecvInvokeDragSession(
const MaybeDiscarded<WindowContext>& aSourceWindowContext,
const MaybeDiscarded<WindowContext>& aSourceTopWindowContext,
nsTArray<IPCTransferableData>&& aTransferables, const uint32_t& aAction) {
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
if (dragService) {
if (nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1")) {
dragService->StartDragSession();
nsCOMPtr<nsIDragSession> session;
dragService->GetCurrentSession(getter_AddRefs(session));
@ -3167,43 +3207,25 @@ mozilla::ipc::IPCResult ContentChild::RecvInvokeDragSession(
session->SetSourceTopWindowContext(
aSourceTopWindowContext.GetMaybeDiscarded());
session->SetDragAction(aAction);
// Check if we are receiving any file objects. If we are we will want
// to hide any of the other objects coming in from content.
bool hasFiles = false;
for (uint32_t i = 0; i < aTransferables.Length() && !hasFiles; ++i) {
auto& items = aTransferables[i].items();
for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) {
if (items[j].data().type() ==
IPCTransferableDataType::TIPCTransferableDataBlob) {
hasFiles = true;
}
}
}
// Add the entries from the IPC to the new DataTransfer
RefPtr<DataTransfer> dataTransfer =
ConvertToDataTransfer(std::move(aTransferables), eDragStart);
session->SetDataTransfer(dataTransfer);
}
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvUpdateDragSession(
nsTArray<IPCTransferableData>&& aTransferables,
EventMessage aEventMessage) {
if (nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1")) {
nsCOMPtr<nsIDragSession> session;
dragService->GetCurrentSession(getter_AddRefs(session));
if (session) {
nsCOMPtr<DataTransfer> dataTransfer =
new DataTransfer(nullptr, eDragStart, false, -1);
for (uint32_t i = 0; i < aTransferables.Length(); ++i) {
auto& items = aTransferables[i].items();
for (uint32_t j = 0; j < items.Length(); ++j) {
const IPCTransferableDataItem& item = items[j];
RefPtr<nsVariantCC> variant = new nsVariantCC();
nsresult rv =
nsContentUtils::IPCTransferableDataItemToVariant(item, variant);
if (NS_FAILED(rv)) {
continue;
}
// We should hide this data from content if we have a file, and we
// aren't a file.
bool hidden =
hasFiles && item.data().type() !=
IPCTransferableDataType::TIPCTransferableDataBlob;
dataTransfer->SetDataWithPrincipalFromOtherProcess(
NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
nsContentUtils::GetSystemPrincipal(), hidden);
}
}
ConvertToDataTransfer(std::move(aTransferables), aEventMessage);
session->SetDataTransfer(dataTransfer);
}
}

Просмотреть файл

@ -415,6 +415,10 @@ class ContentChild final : public PContentChild,
const MaybeDiscarded<WindowContext>& aSourceTopWindowContext,
nsTArray<IPCTransferableData>&& aTransferables, const uint32_t& aAction);
mozilla::ipc::IPCResult RecvUpdateDragSession(
nsTArray<IPCTransferableData>&& aTransferables,
EventMessage aEventMessage);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
mozilla::ipc::IPCResult RecvEndDragSession(
const bool& aDoneDrag, const bool& aUserCancelled,

Просмотреть файл

@ -5358,7 +5358,28 @@ bool ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor) {
}
#endif
void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
void ContentParent::GetIPCTransferableData(
nsIDragSession* aSession, BrowserParent* aParent,
nsTArray<IPCTransferableData>& aIPCTransferables) {
RefPtr<DataTransfer> transfer = aSession->GetDataTransfer();
if (!transfer) {
// Pass eDrop to get DataTransfer with external
// drag formats cached.
transfer = new DataTransfer(nullptr, eDrop, true, -1);
aSession->SetDataTransfer(transfer);
}
// Note, even though this fills the DataTransfer object with
// external data, the data is usually transfered over IPC lazily when
// needed.
transfer->FillAllExternalData();
nsCOMPtr<nsILoadContext> lc = aParent ? aParent->GetLoadContext() : nullptr;
nsCOMPtr<nsIArray> transferables = transfer->GetTransferables(lc);
nsContentUtils::TransferablesToIPCTransferableDatas(
transferables, aIPCTransferables, false, this);
}
void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent,
EventMessage aMessage) {
// dnd uses IPCBlob to transfer data to the content process and the IPC
// message is sent as normal priority. When sending input events with input
// priority, the message may be preempted by the later dnd events. To make
@ -5369,28 +5390,17 @@ void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
if (dragService && dragService->MaybeAddChildProcess(this)) {
// We need to send transferable data to child process.
if (!dragService) {
return;
}
if (dragService->MaybeAddChildProcess(this)) {
nsCOMPtr<nsIDragSession> session;
dragService->GetCurrentSession(getter_AddRefs(session));
if (session) {
// We need to send transferable data to child process.
nsTArray<IPCTransferableData> ipcTransferables;
RefPtr<DataTransfer> transfer = session->GetDataTransfer();
if (!transfer) {
// Pass eDrop to get DataTransfer with external
// drag formats cached.
transfer = new DataTransfer(nullptr, eDrop, true, -1);
session->SetDataTransfer(transfer);
}
// Note, even though this fills the DataTransfer object with
// external data, the data is usually transfered over IPC lazily when
// needed.
transfer->FillAllExternalData();
nsCOMPtr<nsILoadContext> lc =
aParent ? aParent->GetLoadContext() : nullptr;
nsCOMPtr<nsIArray> transferables = transfer->GetTransferables(lc);
nsContentUtils::TransferablesToIPCTransferableDatas(
transferables, ipcTransferables, false, this);
GetIPCTransferableData(session, aParent, ipcTransferables);
uint32_t action;
session->GetDragAction(&action);
@ -5401,6 +5411,19 @@ void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
mozilla::Unused << SendInvokeDragSession(
sourceWC, sourceTopWC, std::move(ipcTransferables), action);
}
return;
}
if (dragService->MustUpdateDataTransfer(aMessage)) {
nsCOMPtr<nsIDragSession> session;
dragService->GetCurrentSession(getter_AddRefs(session));
if (session) {
// We need to send transferable data to child process.
nsTArray<IPCTransferableData> ipcTransferables;
GetIPCTransferableData(session, aParent, ipcTransferables);
mozilla::Unused << SendUpdateDragSession(std::move(ipcTransferables),
aMessage);
}
}
}

Просмотреть файл

@ -463,7 +463,7 @@ class ContentParent final : public PContentParent,
mozilla::ipc::IPCResult RecvNotifyShutdownSuccess();
void MaybeInvokeDragSession(BrowserParent* aParent);
void MaybeInvokeDragSession(BrowserParent* aParent, EventMessage aMessage);
PContentPermissionRequestParent* AllocPContentPermissionRequestParent(
const nsTArray<PermissionRequest>& aRequests, nsIPrincipal* aPrincipal,
@ -1417,6 +1417,9 @@ class ContentParent final : public PContentParent,
return mThreadsafeHandle;
}
void GetIPCTransferableData(nsIDragSession* aSession, BrowserParent* aParent,
nsTArray<IPCTransferableData>& aIPCTransferables);
private:
// Return an existing ContentParent if possible. Otherwise, `nullptr`.
static already_AddRefed<ContentParent> GetUsedBrowserProcess(

Просмотреть файл

@ -98,6 +98,7 @@ using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
using mozilla::EventMessage from "mozilla/EventForwards.h";
using mozilla::LayoutDeviceIntPoint from "Units.h";
using mozilla::ImagePoint from "Units.h";
using mozilla::ImageIntSize from "Units.h";
@ -824,6 +825,8 @@ child:
MaybeDiscardedWindowContext aSourceTopWindowContext,
IPCTransferableData[] transfers, uint32_t action);
async UpdateDragSession(IPCTransferableData[] transfers, EventMessage message);
async EndDragSession(bool aDoneDrag, bool aUserCancelled,
LayoutDeviceIntPoint aDragEndPoint,
uint32_t aKeyModifiers,

Просмотреть файл

@ -186,6 +186,12 @@ nsDragService::UpdateDragImage(nsINode* aImage, int32_t aImageX,
return NS_OK;
}
bool nsDragService::MustUpdateDataTransfer(EventMessage aMessage) {
// Android's drag and drop API sets drop item in drop event.
// So we have to invalidate data transfer cache on drop event.
return aMessage == eDrop;
}
java::sdk::Bitmap::LocalRef nsDragService::CreateDragImage(
nsINode* aNode, const Maybe<CSSIntRegion>& aRegion) {
LayoutDeviceIntRect dragRect;
@ -218,6 +224,8 @@ java::sdk::Bitmap::LocalRef nsDragService::CreateDragImage(
void nsDragService::SetData(nsITransferable* aTransferable) {
mTransferable = aTransferable;
// Reset DataTransfer
mDataTransfer = nullptr;
}
// static

Просмотреть файл

@ -30,6 +30,7 @@ class nsDragService final : public nsBaseDragService {
uint32_t aKeyModifiers) override;
NS_IMETHOD
UpdateDragImage(nsINode* aImage, int32_t aImageX, int32_t aImageY) override;
virtual bool MustUpdateDataTransfer(mozilla::EventMessage aMessage) override;
void SetData(nsITransferable* aTransferable);

Просмотреть файл

@ -1025,6 +1025,10 @@ bool nsBaseDragService::RemoveAllChildProcesses() {
return true;
}
bool nsBaseDragService::MustUpdateDataTransfer(EventMessage aMessage) {
return false;
}
NS_IMETHODIMP
nsBaseDragService::MaybeEditorDeletedSourceNode(Element* aEditingHost) {
// If builtin editor of Blink and WebKit deletes the source node,they retarget

Просмотреть файл

@ -194,6 +194,11 @@ interface nsIDragService : nsISupports
[notxpcom, nostdcall] boolean maybeAddChildProcess(in ContentParentPtr aChild);
[notxpcom, nostdcall] boolean removeAllChildProcesses();
/**
* Retrun true if nsIDragSession's data is updated.
*/
[notxpcom, nostdcall] boolean mustUpdateDataTransfer(in EventMessage aMessage);
/**
* Called when HTMLEditor maybe deleted the source node from the document.
*