diff --git a/dom/base/nsCopySupport.cpp b/dom/base/nsCopySupport.cpp index 98e0e1c9247d..70662e93e07c 100644 --- a/dom/base/nsCopySupport.cpp +++ b/dom/base/nsCopySupport.cpp @@ -845,8 +845,16 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage, // all stored data, before we return. auto clearAfter = MakeScopeExit([&] { if (clipboardData) { - clipboardData->SetMode(DataTransfer::Mode::Protected); - clipboardData->ClearAll(); + clipboardData->Disconnect(); + + // NOTE: Disconnect may not actually clear the DataTransfer if the + // dom.events.dataTransfer.protected.enabled pref is not on, so we make + // sure we clear here, as not clearing could provide the DataTransfer + // access to information from the system clipboard at an arbitrary point + // in the future. + if (originalEventMessage == ePaste) { + clipboardData->ClearAll(); + } } }); diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index 1f87c3c905a3..39bf5779cbb2 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -81,6 +81,23 @@ enum CustomClipboardTypeId { eCustomClipboardTypeId_String }; +// The dom.events.dataTransfer.protected.enabled preference controls whether or +// not the `protected` dataTransfer state is enabled. If the `protected` +// dataTransfer stae is disabled, then the DataTransfer will be read-only +// whenever it should be protected, and will not be disconnected after a drag +// event is completed. +static bool +PrefProtected() +{ + static bool sInitialized = false; + static bool sValue = false; + if (!sInitialized) { + sInitialized = true; + Preferences::AddBoolVarCache(&sValue, "dom.events.dataTransfer.protected.enabled"); + } + return sValue; +} + static DataTransfer::Mode ModeForEvent(EventMessage aEventMessage) { @@ -98,7 +115,9 @@ ModeForEvent(EventMessage aEventMessage) // the DataTransfer, rather than just the type information. return DataTransfer::Mode::ReadOnly; default: - return DataTransfer::Mode::Protected; + return PrefProtected() + ? DataTransfer::Mode::Protected + : DataTransfer::Mode::ReadOnly; } } @@ -1276,6 +1295,15 @@ DataTransfer::ConvertFromVariant(nsIVariant* aVariant, return true; } +void +DataTransfer::Disconnect() +{ + SetMode(Mode::Protected); + if (PrefProtected()) { + ClearAll(); + } +} + void DataTransfer::ClearAll() { @@ -1608,5 +1636,15 @@ DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex, } while (type != eCustomClipboardTypeId_None); } +void +DataTransfer::SetMode(DataTransfer::Mode aMode) +{ + if (!PrefProtected() && aMode == Mode::Protected) { + mMode = Mode::ReadOnly; + } else { + mMode = aMode; + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/events/DataTransfer.h b/dom/events/DataTransfer.h index 6dc787a67aa4..837d5156026d 100644 --- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -229,9 +229,7 @@ public: Mode GetMode() const { return mMode; } - void SetMode(Mode aMode) { - mMode = aMode; - } + void SetMode(Mode aMode); // Helper method. Is true if the DataTransfer's mode is ReadOnly or Protected, // which means that the DataTransfer cannot be modified. @@ -272,6 +270,12 @@ public: nsISupports** aSupports, uint32_t* aLength) const; + // Disconnects the DataTransfer from the Drag Data Store. If the + // dom.dataTransfer.disconnect pref is enabled, this will clear the + // DataTransfer and set it to the `Protected` state, otherwise this method is + // a no-op. + void Disconnect(); + // clears all of the data void ClearAll(); diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 63e6df0f3962..b94dc0c88e01 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -1774,8 +1774,7 @@ EventStateManager::GenerateDragGesture(nsPresContext* aPresContext, new DataTransfer(window, eDragStart, false, -1); auto protectDataTransfer = MakeScopeExit([&] { if (dataTransfer) { - dataTransfer->SetMode(DataTransfer::Mode::Protected); - dataTransfer->ClearAll(); + dataTransfer->Disconnect(); } }); diff --git a/dom/events/test/test_bug508479.html b/dom/events/test/test_bug508479.html index a2be834abcc4..9cf9e22b8a1d 100644 --- a/dom/events/test/test_bug508479.html +++ b/dom/events/test/test_bug508479.html @@ -74,7 +74,7 @@ function runTests() SpecialPowers.addChromeEventListener("drop", chromeListener, true, false); var targetNotHandling = document.getElementById("nothandling_target"); - fireDrop(targetNotHandling, false, true); + fireDrop(targetNotHandling, true, true); SpecialPowers.removeChromeEventListener("drop", chromeListener, true); ok(chromeGotEvent, "Chrome should have got drop event!"); is(gGotHandlingDrop, false, "Didn't get drop on accepting element (2)"); diff --git a/dom/tests/mochitest/general/test_clipboard_events.html b/dom/tests/mochitest/general/test_clipboard_events.html index dd8df0af9c72..8b7f4a4d9705 100644 --- a/dom/tests/mochitest/general/test_clipboard_events.html +++ b/dom/tests/mochitest/general/test_clipboard_events.html @@ -746,7 +746,13 @@ function test_image_dataTransfer() { } } -SimpleTest.waitForFocus(doTests); +SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({ + // NOTE: These tests operate under the assumption that the protected mode of + // DataTransfer is enabled. + "set": [["dom.events.dataTransfer.protected.enabled", true]] + }, doTests); +}); diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index d6cfe70e2c96..64bd5c771f4d 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -8246,8 +8246,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, // made protected, and then disconnected. DataTransfer* dataTransfer = aEvent->AsDragEvent()->mDataTransfer; if (dataTransfer) { - dataTransfer->SetMode(DataTransfer::Mode::Protected); - dataTransfer->ClearAll(); + dataTransfer->Disconnect(); } break; }