Bug 1918027 part 3 - share DataTransfer between clipboard event and calls to HandlePaste() r=edgar,dlp-reviewers,masayuki,handyman

This handles a race condition so if the user copies new data to the clipboard
while Content Analysis is ongoing, the original data will be used in the
paste action.

Differential Revision: https://phabricator.services.mozilla.com/D228719
This commit is contained in:
Greg Stoll
2024-12-04 22:58:30 +00:00
parent 73bb49f875
commit 75942a02c0
19 changed files with 495 additions and 188 deletions

View File

@@ -1414,6 +1414,7 @@ Document::Document(const char* aContentType)
mForceLoadAtTop(false),
mFireMutationEvents(true),
mHasPolicyWithRequireTrustedTypesForDirective(false),
mClipboardCopyTriggered(false),
mXMLDeclarationBits(0),
mOnloadBlockCount(0),
mWriteLevel(0),

View File

@@ -3688,6 +3688,9 @@ class Document : public nsINode,
mHasPolicyWithRequireTrustedTypesForDirective =
aHasPolicyWithRequireTrustedTypesForDirective;
}
bool IsClipboardCopyTriggered() const { return mClipboardCopyTriggered; }
void ClearClipboardCopyTriggered() { mClipboardCopyTriggered = false; }
void SetClipboardCopyTriggered() { mClipboardCopyTriggered = true; }
// Even if mutation events are disabled by default,
// dom.mutation_events.forceEnable can be used to enable them per site.
@@ -4966,6 +4969,10 @@ class Document : public nsINode,
// Whether the document's CSP contains a require-trusted-types-for directive.
bool mHasPolicyWithRequireTrustedTypesForDirective : 1;
// Whether a copy event happened. Used to detect when this happens
// while a paste event is being handled in JS.
bool mClipboardCopyTriggered : 1;
Maybe<bool> mMutationEventsEnabled;
// The fingerprinting protections overrides for this document. The value will

View File

@@ -758,7 +758,8 @@ class MOZ_RAII AutoHandlingPasteEvent final {
bool nsCopySupport::FireClipboardEvent(
EventMessage aEventMessage,
mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType,
PresShell* aPresShell, Selection* aSelection, bool* aActionTaken) {
PresShell* aPresShell, Selection* aSelection, DataTransfer* aDataTransfer,
bool* aActionTaken) {
if (aActionTaken) {
*aActionTaken = false;
}
@@ -825,9 +826,18 @@ bool nsCopySupport::FireClipboardEvent(
bool doDefault = true;
RefPtr<DataTransfer> clipboardData;
if (chromeShell || StaticPrefs::dom_event_clipboardevents_enabled()) {
clipboardData =
new DataTransfer(doc->GetScopeObject(), aEventMessage,
originalEventMessage == ePaste, aClipboardType);
MOZ_ASSERT_IF(aDataTransfer,
aDataTransfer->GetParentObject() == doc->GetScopeObject());
MOZ_ASSERT_IF(aDataTransfer, (aDataTransfer->GetEventMessage() == ePaste) &&
(aEventMessage == ePaste ||
aEventMessage == ePasteNoFormatting));
MOZ_ASSERT_IF(aDataTransfer,
aDataTransfer->ClipboardType() == aClipboardType);
clipboardData = aDataTransfer
? RefPtr<DataTransfer>(aDataTransfer)
: MakeRefPtr<DataTransfer>(
doc->GetScopeObject(), aEventMessage,
originalEventMessage == ePaste, aClipboardType);
nsEventStatus status = nsEventStatus_eIgnore;
InternalClipboardEvent evt(true, originalEventMessage);
@@ -851,7 +861,7 @@ bool nsCopySupport::FireClipboardEvent(
// our DataTransfer, which means setting its mode to `Protected` and clearing
// all stored data, before we return.
auto clearAfter = MakeScopeExit([&] {
if (clipboardData) {
if (clipboardData && !aDataTransfer) {
clipboardData->Disconnect();
// NOTE: Disconnect may not actually clear the DataTransfer if the
@@ -959,6 +969,10 @@ bool nsCopySupport::FireClipboardEvent(
// the effect of updating the enabled state of the paste menu item.
if (doDefault || count) {
piWindow->UpdateCommands(u"clipboard"_ns);
if (aPresShell && aPresShell->GetDocument()) {
// Record that a copy to the clipboard was triggered by JS code
aPresShell->GetDocument()->SetClipboardCopyTriggered();
}
}
if (aActionTaken) {

View File

@@ -23,6 +23,7 @@ class nsILoadContext;
namespace mozilla {
class PresShell;
namespace dom {
class DataTransfer;
class Document;
class Selection;
class WindowContext;
@@ -104,6 +105,10 @@ class nsCopySupport {
*
* aClipboardType specifies which clipboard to use, from nsIClipboard.
*
* If aDataTransfer is non-NULL, that data will be used to fire the clipboard
* event, and the caller is responsible for calling Disconnect(). (and
* possibly ClearAll())
*
* If aActionTaken is non-NULL, it will be set to true if an action was
* taken, whether it be the default action or the default being prevented.
*
@@ -114,7 +119,7 @@ class nsCopySupport {
mozilla::EventMessage aEventMessage,
mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType,
mozilla::PresShell* aPresShell, mozilla::dom::Selection* aSelection,
bool* aActionTaken = nullptr);
mozilla::dom::DataTransfer* aDataTransfer, bool* aActionTaken = nullptr);
};
#endif

View File

@@ -578,7 +578,7 @@ nsresult nsClipboardCommand::DoCommand(const char* aCommandName,
bool actionTaken = false;
nsCopySupport::FireClipboardEvent(eventMessage,
Some(nsIClipboard::kGlobalClipboard),
presShell, nullptr, &actionTaken);
presShell, nullptr, nullptr, &actionTaken);
return actionTaken ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
}

View File

@@ -1668,4 +1668,21 @@ void DataTransfer::ClearForPaste() {
ClearAll();
}
bool DataTransfer::HasPrivateHTMLFlavor() const {
MOZ_ASSERT(mEventMessage == ePaste,
"Only works for ePaste messages, where the mClipboardDataSnapshot "
"is available.");
nsIClipboardDataSnapshot* snapshot = GetClipboardDataSnapshot();
if (!snapshot) {
NS_WARNING("DataTransfer::GetClipboardDataSnapshot() returned null");
return false;
}
nsTArray<nsCString> snapshotFlavors;
if (NS_FAILED(snapshot->GetFlavorList(snapshotFlavors))) {
NS_WARNING("nsIClipboardDataSnapshot::GetFlavorList() failed");
return false;
}
return snapshotFlavors.Contains(kHTMLContext);
}
} // namespace mozilla::dom

View File

@@ -447,6 +447,8 @@ class DataTransfer final : public nsISupports, public nsWrapperCache {
// Clears this DataTransfer that was used for paste
void ClearForPaste();
bool HasPrivateHTMLFlavor() const;
protected:
// Retrieve a list of clipboard formats supported
//

View File

@@ -6191,11 +6191,25 @@ nsresult EventStateManager::HandleMiddleClickPaste(
clipboardType = nsIClipboard::kSelectionClipboard;
}
RefPtr<DataTransfer> dataTransfer;
if (aEditorBase) {
// Create the same DataTransfer object here so we can share it between
// the clipboard event and the call to HandlePaste below. This prevents
// race conditions with Content Analysis on like we see in bug 1918027.
dataTransfer =
aEditorBase->CreateDataTransferForPaste(ePaste, clipboardType);
}
const auto clearDataTransfer = MakeScopeExit([&] {
if (dataTransfer) {
dataTransfer->ClearForPaste();
}
});
// Fire ePaste event by ourselves since we need to dispatch "paste" event
// even if the middle click event was consumed for compatibility with
// Chromium.
if (!nsCopySupport::FireClipboardEvent(ePaste, Some(clipboardType),
aPresShell, selection)) {
aPresShell, selection, dataTransfer)) {
*aStatus = nsEventStatus_eConsumeNoDefault;
return NS_OK;
}
@@ -6230,11 +6244,11 @@ nsresult EventStateManager::HandleMiddleClickPaste(
// quotation. Otherwise, paste it as is.
if (aMouseEvent->IsControl()) {
DebugOnly<nsresult> rv = aEditorBase->PasteAsQuotationAsAction(
clipboardType, EditorBase::DispatchPasteEvent::No);
clipboardType, EditorBase::DispatchPasteEvent::No, dataTransfer);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste as quotation");
} else {
DebugOnly<nsresult> rv = aEditorBase->PasteAsAction(
clipboardType, EditorBase::DispatchPasteEvent::No);
clipboardType, EditorBase::DispatchPasteEvent::No, dataTransfer);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste");
}
*aStatus = nsEventStatus_eConsumeNoDefault;

View File

@@ -76,6 +76,7 @@
#include "mozilla/dom/BorrowedAttrInfo.h" // for BorrowedAttrInfo
#include "mozilla/dom/BrowsingContext.h" // for BrowsingContext
#include "mozilla/dom/CharacterData.h" // for CharacterData
#include "mozilla/dom/ContentParent.h" // for ContentParent
#include "mozilla/dom/DataTransfer.h" // for DataTransfer
#include "mozilla/dom/Document.h" // for Document
#include "mozilla/dom/DocumentInlines.h" // for GetObservingPresShell
@@ -1584,10 +1585,26 @@ bool EditorBase::CheckForClipboardCommandListener(
return false;
}
already_AddRefed<DataTransfer> EditorBase::CreateDataTransferForPaste(
EventMessage aEventMessage,
nsIClipboard::ClipboardType aClipboardType) const {
nsIGlobalObject* scopeObject = nullptr;
if (PresShell* presShell = GetPresShell()) {
if (Document* doc = presShell->GetDocument()) {
scopeObject = doc->GetScopeObject();
}
}
auto dataTransfer = MakeRefPtr<DataTransfer>(scopeObject, aEventMessage, true,
Some(aClipboardType));
return dataTransfer.forget();
}
Result<EditorBase::ClipboardEventResult, nsresult>
EditorBase::DispatchClipboardEventAndUpdateClipboard(
EventMessage aEventMessage,
Maybe<nsIClipboard::ClipboardType> aClipboardType) {
Maybe<nsIClipboard::ClipboardType> aClipboardType,
DataTransfer* aDataTransfer /* = nullptr */) {
MOZ_ASSERT(IsEditActionDataAvailable());
const bool isPasting =
@@ -1617,7 +1634,8 @@ EditorBase::DispatchClipboardEventAndUpdateClipboard(
bool actionTaken = false;
const bool doDefault = nsCopySupport::FireClipboardEvent(
aEventMessage, aClipboardType, presShell, sel, &actionTaken);
aEventMessage, aClipboardType, presShell, sel, aDataTransfer,
&actionTaken);
NotifyOfDispatchingClipboardEvent();
if (NS_WARN_IF(Destroyed())) {
@@ -1812,13 +1830,33 @@ NS_IMETHODIMP EditorBase::Paste(nsIClipboard::ClipboardType aClipboardType) {
nsresult EditorBase::PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer /* = nullptr */,
nsIPrincipal* aPrincipal /* = nullptr */) {
if (IsHTMLEditor() && IsReadonly()) {
return NS_OK;
}
// Create the same DataTransfer object here so we can share it between
// the clipboard event and the call to HandlePaste below. This prevents
// race conditions with Content Analysis on like we see in bug 1918027.
// Note that this is not needed if we're not going to dispatch the paste
// event.
RefPtr<DataTransfer> dataTransfer;
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
dataTransfer = aDataTransfer
? RefPtr<DataTransfer>(aDataTransfer)
: RefPtr<DataTransfer>(CreateDataTransferForPaste(
ePaste, aClipboardType));
}
AutoEditActionDataSetter editActionData(*this, EditAction::ePaste,
aPrincipal);
const auto clearDataTransfer = MakeScopeExit([&] {
// If the caller passed in aDataTransfer, they are responsible for clearing
// this.
if (!aDataTransfer && dataTransfer) {
dataTransfer->ClearForPaste();
}
});
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
@@ -1830,13 +1868,22 @@ nsresult EditorBase::PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
}
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
Result<ClipboardEventResult, nsresult> ret =
DispatchClipboardEventAndUpdateClipboard(ePaste, Some(aClipboardType));
if (MOZ_UNLIKELY(ret.isErr())) {
NS_WARNING(
"EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
"failed");
return EditorBase::ToGenericNSResult(ret.unwrapErr());
Result<ClipboardEventResult, nsresult> ret = Err(NS_ERROR_FAILURE);
{
// This method is not set up to pass back the new aDataTransfer
// if it changes. If we need this in the future, we can change
// aDataTransfer to be a RefPtr<DataTransfer>*.
MOZ_ASSERT(!aDataTransfer);
AutoTrackDataTransferForPaste trackDataTransfer(*this, dataTransfer);
ret = DispatchClipboardEventAndUpdateClipboard(
ePaste, Some(aClipboardType), dataTransfer);
if (MOZ_UNLIKELY(ret.isErr())) {
NS_WARNING(
"EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
"failed");
return EditorBase::ToGenericNSResult(ret.unwrapErr());
}
}
switch (ret.inspect()) {
case ClipboardEventResult::DoDefault:
@@ -1865,7 +1912,7 @@ nsresult EditorBase::PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
}
if (editorBase != this) {
nsresult rv = editorBase->PasteAsAction(
aClipboardType, DispatchPasteEvent::No, aPrincipal);
aClipboardType, DispatchPasteEvent::No, dataTransfer, aPrincipal);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::PasteAsAction(DispatchPasteEvent::No) failed");
@@ -1876,8 +1923,7 @@ nsresult EditorBase::PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
// The caller must already have dispatched a "paste" event.
editActionData.NotifyOfDispatchingClipboardEvent();
}
nsresult rv = HandlePaste(editActionData, aClipboardType);
nsresult rv = HandlePaste(editActionData, aClipboardType, dataTransfer);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::HandlePaste() failed");
return EditorBase::ToGenericNSResult(rv);
}
@@ -1885,6 +1931,7 @@ nsresult EditorBase::PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
nsresult EditorBase::PasteAsQuotationAsAction(
nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer /* = nullptr */,
nsIPrincipal* aPrincipal /* = nullptr */) {
MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
aClipboardType == nsIClipboard::kSelectionClipboard);
@@ -1893,6 +1940,26 @@ nsresult EditorBase::PasteAsQuotationAsAction(
return NS_OK;
}
// Create the same DataTransfer object here so we can share it between
// the clipboard event and the call to HandlePasteAsQuotation below. This
// prevents race conditions with Content Analysis on like we see in bug
// 1918027.
// Note that this is not needed if we're not going to dispatch the paste
// event.
RefPtr<DataTransfer> dataTransfer;
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
dataTransfer = aDataTransfer
? RefPtr<DataTransfer>(aDataTransfer)
: RefPtr<DataTransfer>(CreateDataTransferForPaste(
ePaste, aClipboardType));
}
const auto clearDataTransfer = MakeScopeExit([&] {
// If the caller passed in aDataTransfer, they are responsible for clearing
// this.
if (!aDataTransfer && dataTransfer) {
dataTransfer->ClearForPaste();
}
});
AutoEditActionDataSetter editActionData(*this, EditAction::ePasteAsQuotation,
aPrincipal);
if (NS_WARN_IF(!editActionData.CanHandle())) {
@@ -1906,13 +1973,22 @@ nsresult EditorBase::PasteAsQuotationAsAction(
}
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
Result<ClipboardEventResult, nsresult> ret =
DispatchClipboardEventAndUpdateClipboard(ePaste, Some(aClipboardType));
if (MOZ_UNLIKELY(ret.isErr())) {
NS_WARNING(
"EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
"failed");
return EditorBase::ToGenericNSResult(ret.unwrapErr());
Result<ClipboardEventResult, nsresult> ret = Err(NS_ERROR_FAILURE);
{
// This method is not set up to pass back the new aDataTransfer
// if it changes. If we need this in the future, we can change
// aDataTransfer to be a RefPtr<DataTransfer>*.
MOZ_ASSERT(!aDataTransfer);
AutoTrackDataTransferForPaste trackDataTransfer(*this, dataTransfer);
ret = DispatchClipboardEventAndUpdateClipboard(
ePaste, Some(aClipboardType), dataTransfer);
if (MOZ_UNLIKELY(ret.isErr())) {
NS_WARNING(
"EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
"failed");
return EditorBase::ToGenericNSResult(ret.unwrapErr());
}
}
switch (ret.inspect()) {
case ClipboardEventResult::DoDefault:
@@ -1941,7 +2017,7 @@ nsresult EditorBase::PasteAsQuotationAsAction(
}
if (editorBase != this) {
nsresult rv = editorBase->PasteAsQuotationAsAction(
aClipboardType, DispatchPasteEvent::No, aPrincipal);
aClipboardType, DispatchPasteEvent::No, dataTransfer, aPrincipal);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::PasteAsQuotationAsAction("
"DispatchPasteEvent::No) failed");
@@ -1953,7 +2029,8 @@ nsresult EditorBase::PasteAsQuotationAsAction(
editActionData.NotifyOfDispatchingClipboardEvent();
}
nsresult rv = HandlePasteAsQuotation(editActionData, aClipboardType);
nsresult rv =
HandlePasteAsQuotation(editActionData, aClipboardType, dataTransfer);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::HandlePasteAsQuotation() failed");
return EditorBase::ToGenericNSResult(rv);
@@ -6568,7 +6645,7 @@ void EditorBase::AutoEditActionDataSetter::InitializeDataTransfer(
}
void EditorBase::AutoEditActionDataSetter::InitializeDataTransferWithClipboard(
SettingDataTransfer aSettingDataTransfer,
SettingDataTransfer aSettingDataTransfer, DataTransfer* aDataTransfer,
nsIClipboard::ClipboardType aClipboardType) {
MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
"It's too late to set dataTransfer since this may have already "
@@ -6580,12 +6657,22 @@ void EditorBase::AutoEditActionDataSetter::InitializeDataTransferWithClipboard(
// mDataTransfer will be used for eEditorInput event, but we can keep
// using ePaste and ePasteNoFormatting here. If we need to use eEditorInput,
// we need to create eEditorInputNoFormatting or something...
mDataTransfer =
new DataTransfer(scopeObject,
aSettingDataTransfer == SettingDataTransfer::eWithFormat
? ePaste
: ePasteNoFormatting,
true /* is external */, Some(aClipboardType));
EventMessage message =
(aSettingDataTransfer == SettingDataTransfer::eWithFormat)
? ePaste
: ePasteNoFormatting;
if (aDataTransfer) {
// The DataTransfer being passed in will be used in a paste event, which
// means it will be cleared after that event is done firing. We don't want
// that for "input" and "beforeinput" events, so make a copy of its data.
aDataTransfer->Clone(scopeObject, message,
/* aUserCancelled = */ false,
/* aIsCrossDomainSubFrameDrop = */ false,
getter_AddRefs(mDataTransfer));
} else {
mDataTransfer = MakeRefPtr<DataTransfer>(
scopeObject, message, true /* is external */, Some(aClipboardType));
}
}
void EditorBase::AutoEditActionDataSetter::AppendTargetRange(
@@ -7165,4 +7252,102 @@ void EditorBase::StopPreservingSelection() {
SavedSelectionRef().RemoveAllRanges();
}
nsresult EditorBase::GetDataFromDataTransferOrClipboard(
DataTransfer* aDataTransfer, nsITransferable* aTransferable,
nsIClipboard::ClipboardType aClipboardType) const {
MOZ_ASSERT(aTransferable);
if (aDataTransfer) {
MOZ_ASSERT(aDataTransfer->ClipboardType() == Some(aClipboardType));
nsresult rv = [aDataTransfer, aTransferable]() -> nsresult {
nsIClipboardDataSnapshot* snapshot =
aDataTransfer->GetClipboardDataSnapshot();
MOZ_ASSERT(snapshot);
bool snapshotIsValid = false;
snapshot->GetValid(&snapshotIsValid);
if (!snapshotIsValid) {
NS_WARNING("DataTransfer::GetClipboardDataSnapshot() is not valid");
return NS_ERROR_FAILURE;
}
AutoTArray<nsCString, 10> transferableFlavors;
nsresult rv =
aTransferable->FlavorsTransferableCanImport(transferableFlavors);
if (NS_FAILED(rv)) {
NS_WARNING("nsITransferable::FlavorsTransferableCanImport() failed");
return rv;
}
if (transferableFlavors.Length() == 1) {
// avoid creating unneeded temporary transferables
rv = snapshot->GetDataSync(aTransferable);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"nsIClipboardDataSnapshot::GetDataSync() failed");
return rv;
}
AutoTArray<nsCString, 5> snapshotFlavors;
rv = snapshot->GetFlavorList(snapshotFlavors);
if (NS_FAILED(rv)) {
NS_WARNING("nsIClipboardDataSnapshot::GetFlavorList() failed");
return rv;
}
for (const auto& transferableFlavor : transferableFlavors) {
if (snapshotFlavors.Contains(transferableFlavor)) {
AutoTArray<nsCString, 1> singleTypeArray{transferableFlavor};
auto singleTransferableToCheck =
ContentParent::CreateClipboardTransferable(singleTypeArray);
if (singleTransferableToCheck.isErr()) {
NS_WARNING("Failed to CreateClipboardTransferable()");
return singleTransferableToCheck.unwrapErr();
}
nsCOMPtr<nsITransferable> singleTransferable =
singleTransferableToCheck.unwrap();
rv = snapshot->GetDataSync(singleTransferable);
if (NS_FAILED(rv)) {
NS_WARNING("nsIClipboardDataSnapshot::GetDataSync() failed");
return rv;
}
nsCOMPtr<nsISupports> data;
rv = singleTransferable->GetTransferData(transferableFlavor.get(),
getter_AddRefs(data));
if (NS_FAILED(rv)) {
NS_WARNING("nsITransferable::GetTransferData() failed");
return rv;
}
rv = aTransferable->SetTransferData(transferableFlavor.get(), data);
if (NS_FAILED(rv)) {
NS_WARNING("nsITransferable::SetTransferData() failed");
return rv;
}
return NS_OK;
}
}
// Couldn't find any data so return an error so we try the fallback.
return NS_ERROR_FAILURE;
}();
if (NS_SUCCEEDED(rv)) {
return NS_OK;
}
// If this fails, the snapshot may have become invalidated. Fall back
// to getting data from the clipboard.
}
// Get Clipboard Service
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard =
do_GetService("@mozilla.org/widget/clipboard;1", &rv);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get nsIClipboard service");
return rv;
}
auto* windowContext = GetDocument()->GetWindowContext();
if (!windowContext) {
NS_WARNING("No window context");
return NS_ERROR_FAILURE;
}
rv = clipboard->GetData(aTransferable, aClipboardType, windowContext);
if (NS_FAILED(rv)) {
NS_WARNING("nsIClipboard::GetData() failed");
return rv;
}
return NS_OK;
}
} // namespace mozilla

View File

@@ -108,6 +108,7 @@ class EditorBase : public nsIEditor,
* method to edit the DOM tree, you can make your new method public.
****************************************************************************/
using DataTransfer = dom::DataTransfer;
using Document = dom::Document;
using Element = dom::Element;
using InterlinePosition = dom::Selection::InterlinePosition;
@@ -241,6 +242,14 @@ class EditorBase : public nsIEditor,
return selection ? selection->GetAncestorLimiter() : nullptr;
}
/**
* Create a DataTransfer object that can be shared between the paste event
* and pasting into a DOM element.
*/
already_AddRefed<DataTransfer> CreateDataTransferForPaste(
EventMessage aEventMessage,
nsIClipboard::ClipboardType aClipboardType) const;
/**
* Fast non-refcounting editor root element accessor
*/
@@ -752,6 +761,9 @@ class EditorBase : public nsIEditor,
* nsIClipboard::kSelectionClipboard.
* @param aDispatchPasteEvent Yes if this should dispatch ePaste event
* before pasting. Otherwise, No.
* @param aDataTransfer The object containing the data to use for the
* paste operation. May be nullptr, in which case
* this will just get the data from the clipboard.
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
@@ -760,6 +772,7 @@ class EditorBase : public nsIEditor,
MOZ_CAN_RUN_SCRIPT nsresult
PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer = nullptr,
nsIPrincipal* aPrincipal = nullptr);
/**
@@ -787,6 +800,9 @@ class EditorBase : public nsIEditor,
* nsIClipboard::kSelectionClipboard.
* @param aDispatchPasteEvent Yes if this should dispatch ePaste event
* before pasting. Otherwise, No.
* @param aDataTransfer The object containing the data to use for the
* paste operation. May be nullptr, in which case
* this will just get the data from the clipboard.
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
@@ -794,6 +810,7 @@ class EditorBase : public nsIEditor,
MOZ_CAN_RUN_SCRIPT nsresult
PasteAsQuotationAsAction(nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer = nullptr,
nsIPrincipal* aPrincipal = nullptr);
/**
@@ -1151,7 +1168,7 @@ class EditorBase : public nsIEditor,
* because it'll be set to InputEvent.dataTransfer and which should be
* read-only.
*/
void InitializeDataTransfer(dom::DataTransfer* aDataTransfer);
void InitializeDataTransfer(DataTransfer* aDataTransfer);
/**
* InitializeDataTransfer(nsITransferable*) creates new DataTransfer
* instance, initializes it with aTransferable and sets mDataTransfer to
@@ -1168,9 +1185,9 @@ class EditorBase : public nsIEditor,
* initializes it with clipboard and sets mDataTransfer to it.
*/
void InitializeDataTransferWithClipboard(
SettingDataTransfer aSettingDataTransfer,
SettingDataTransfer aSettingDataTransfer, DataTransfer* aDataTransfer,
nsIClipboard::ClipboardType aClipboardType);
dom::DataTransfer* GetDataTransfer() const { return mDataTransfer; }
DataTransfer* GetDataTransfer() const { return mDataTransfer; }
/**
* AppendTargetRange() appends aTargetRange to target ranges. This should
@@ -1420,7 +1437,7 @@ class EditorBase : public nsIEditor,
nsString mData;
// The dataTransfer should be set to InputEvent.dataTransfer.
RefPtr<dom::DataTransfer> mDataTransfer;
RefPtr<DataTransfer> mDataTransfer;
// They are used for result of InputEvent.getTargetRanges() of beforeinput.
OwningNonNullStaticRangeArray mTargetRanges;
@@ -1541,6 +1558,10 @@ class EditorBase : public nsIEditor,
return mEditActionData->IsAborted();
}
nsresult GetDataFromDataTransferOrClipboard(
DataTransfer* aDataTransfer, nsITransferable* aTransferable,
nsIClipboard::ClipboardType aClipboardType) const;
/**
* SelectionRef() returns cached normal Selection. This is pretty faster than
* EditorBase::GetSelection() if available.
@@ -1582,7 +1603,7 @@ class EditorBase : public nsIEditor,
* content with current edit action. The result is proper for
* InputEvent.dataTransfer value.
*/
dom::DataTransfer* GetInputEventDataTransfer() const {
DataTransfer* GetInputEventDataTransfer() const {
return mEditActionData ? mEditActionData->GetDataTransfer() : nullptr;
}
@@ -2558,7 +2579,7 @@ class EditorBase : public nsIEditor,
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult
InsertDroppedDataTransferAsAction(AutoEditActionDataSetter& aEditActionData,
dom::DataTransfer& aDataTransfer,
DataTransfer& aDataTransfer,
const EditorDOMPoint& aDroppedAt,
nsIPrincipal* aSourcePrincipal) = 0;
@@ -2718,7 +2739,8 @@ class EditorBase : public nsIEditor,
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<ClipboardEventResult, nsresult>
DispatchClipboardEventAndUpdateClipboard(
EventMessage aEventMessage,
mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType);
mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType,
DataTransfer* aDataTransfer = nullptr);
/**
* Called after PasteAsAction() dispatches "paste" event and it's not
@@ -2726,7 +2748,8 @@ class EditorBase : public nsIEditor,
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult HandlePaste(
AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) = 0;
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) = 0;
/**
* Called after PasteAsQuotationAsAction() dispatches "paste" event and it's
@@ -2734,11 +2757,12 @@ class EditorBase : public nsIEditor,
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult HandlePasteAsQuotation(
AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) = 0;
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) = 0;
/**
* Called after PasteTransferableAsAction() dispatches "paste" event and it's
* not canceled.
* Called after PasteTransferableAsAction() dispatches "paste" event and
* it's not canceled.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult HandlePasteTransferable(
AutoEditActionDataSetter& aEditActionData,
@@ -2759,17 +2783,17 @@ class EditorBase : public nsIEditor,
protected: // helper classes which may be used by friends
/**
* Stack based helper class for batching a collection of transactions inside
* a placeholder transaction. Different from AutoTransactionBatch, this
* notifies editor observers of before/end edit action handling, and
* Stack based helper class for batching a collection of transactions
* inside a placeholder transaction. Different from AutoTransactionBatch,
* this notifies editor observers of before/end edit action handling, and
* dispatches "input" event if it's necessary.
*/
class MOZ_RAII AutoPlaceholderBatch final {
public:
/**
* @param aRequesterFuncName function name which wants to end the batch.
* This won't be stored nor exposed to selection listeners etc, used only
* for logging. This MUST be alive when the destructor runs.
* This won't be stored nor exposed to selection listeners etc, used
* only for logging. This MUST be alive when the destructor runs.
*/
AutoPlaceholderBatch(EditorBase& aEditorBase,
ScrollSelectionIntoView aScrollSelectionIntoView,
@@ -2813,8 +2837,10 @@ class EditorBase : public nsIEditor,
EditorBase& aEditorBase, EditSubAction aEditSubAction,
nsIEditor::EDirection aDirection, ErrorResult& aRv)
: mEditorBase(aEditorBase), mIsTopLevel(true) {
// The top level edit sub action has already be set if this is nested call
// XXX Looks like that this is not aware of unexpected nested edit action
// The top level edit sub action has already be set if this is nested
// call
// XXX Looks like that this is not aware of unexpected nested edit
// action
// handling via selectionchange event listener or mutation event
// listener.
if (!mEditorBase.GetTopLevelEditSubAction()) {
@@ -2869,8 +2895,8 @@ class EditorBase : public nsIEditor,
public:
/**
* @param aRequesterFuncName function name which wants to end the batch.
* This won't be stored nor exposed to selection listeners etc, used only
* for logging. This MUST be alive when the destructor runs.
* This won't be stored nor exposed to selection listeners etc, used
* only for logging. This MUST be alive when the destructor runs.
*/
MOZ_CAN_RUN_SCRIPT explicit AutoUpdateViewBatch(
EditorBase& aEditorBase, const char* aRequesterFuncName)
@@ -2925,8 +2951,8 @@ class EditorBase : public nsIEditor,
mutable nsString mCachedDocumentEncoderType;
// Listens to all low level actions on the doc.
// Edit action listener is currently used by highlighter of the findbar and
// the spellchecker. So, we should reserve only 2 items.
// Edit action listener is currently used by highlighter of the findbar
// and the spellchecker. So, we should reserve only 2 items.
using AutoActionListenerArray =
AutoTArray<OwningNonNull<nsIEditActionListener>, 2>;
AutoActionListenerArray mActionListeners;
@@ -2981,9 +3007,10 @@ class EditorBase : public nsIEditor,
friend class AutoSelectionRestorer; // RangeUpdaterRef, SavedSelectionRef
friend class CaretPoint; // AllowsTransactionsToChangeSelection,
// CollapseSelectionTo
friend class CompositionTransaction; // CollapseSelectionTo, DoDeleteText,
// DoInsertText, DoReplaceText,
// HideCaret, RangeupdaterRef
friend class CompositionTransaction; // CollapseSelectionTo,
// DoDeleteText, DoInsertText,
// DoReplaceText, HideCaret,
// RangeupdaterRef
friend class DeleteNodeTransaction; // RangeUpdaterRef
friend class DeleteRangeTransaction; // AllowsTransactionsToChangeSelection,
// CollapseSelectionTo

View File

@@ -468,7 +468,7 @@ nsresult PasteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
nsIPrincipal* aPrincipal) const {
nsresult rv = aEditorBase.PasteAsAction(nsIClipboard::kGlobalClipboard,
EditorBase::DispatchPasteEvent::Yes,
aPrincipal);
nullptr, aPrincipal);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::PasteAsAction(nsIClipboard::"
"kGlobalClipboard, DispatchPasteEvent::Yes) failed");
@@ -963,7 +963,7 @@ nsresult PasteQuotationCommand::DoCommand(Command aCommand,
nsIPrincipal* aPrincipal) const {
nsresult rv = aEditorBase.PasteAsQuotationAsAction(
nsIClipboard::kGlobalClipboard, EditorBase::DispatchPasteEvent::Yes,
aPrincipal);
nullptr, aPrincipal);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::PasteAsQuotationAsAction(nsIClipboard::"
"kGlobalClipboard, DispatchPasteEvent::Yes) failed");

View File

@@ -12,6 +12,7 @@
#include "mozilla/IntegerRange.h" // for IntegerRange
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/Result.h" // for Result<>
#include "mozilla/dom/DataTransfer.h" // for dom::DataTransfer
#include "mozilla/dom/Element.h" // for dom::Element
#include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement
#include "mozilla/dom/Selection.h" // for dom::Selection
@@ -343,6 +344,43 @@ class MOZ_STACK_CLASS AutoSelectionRangeArray final {
AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges;
};
/******************************************************************************
* AutoTrackDataTransferForPaste keeps track of whether the paste event handler
* in JS has modified the clipboard.
*****************************************************************************/
class MOZ_STACK_CLASS AutoTrackDataTransferForPaste {
public:
MOZ_CAN_RUN_SCRIPT AutoTrackDataTransferForPaste(
const EditorBase& aEditorBase,
RefPtr<dom::DataTransfer>& aDataTransferForPaste)
: mEditorBase(aEditorBase),
mDataTransferForPaste(aDataTransferForPaste.get_address()) {
mEditorBase.GetDocument()->ClearClipboardCopyTriggered();
}
~AutoTrackDataTransferForPaste() { FlushAndStopTracking(); }
private:
void FlushAndStopTracking() {
if (!mDataTransferForPaste ||
!mEditorBase.GetDocument()->IsClipboardCopyTriggered()) {
return;
}
// The paste event copied new data to the clipboard, so we need to use
// that data to paste into the DOM element below.
if (*mDataTransferForPaste) {
(*mDataTransferForPaste)->ClearForPaste();
}
// Just null this out so this data won't be used and we will get it directly
// from the clipboard in the future.
*mDataTransferForPaste = nullptr;
mDataTransferForPaste = nullptr;
}
MOZ_KNOWN_LIVE const EditorBase& mEditorBase;
RefPtr<dom::DataTransfer>* mDataTransferForPaste;
};
class EditorUtils final {
public:
using EditorType = EditorBase::EditorType;

View File

@@ -241,6 +241,9 @@ class HTMLEditor final : public EditorBase,
* nsIClipboard::kSelectionClipboard.
* @param aDispatchPasteEvent Yes if this should dispatch ePaste event
* before pasting. Otherwise, No.
* @param aDataTransfer The object containing the data to use for the
* paste operation. May be nullptr, in which case
* this will just get the data from the clipboard.
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
@@ -248,6 +251,7 @@ class HTMLEditor final : public EditorBase,
MOZ_CAN_RUN_SCRIPT nsresult
PasteNoFormattingAsAction(nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer = nullptr,
nsIPrincipal* aPrincipal = nullptr);
bool CanPasteTransferable(nsITransferable* aTransferable) final;
@@ -1096,9 +1100,8 @@ class HTMLEditor final : public EditorBase,
SelectionHandling aSelectionHandling) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertDroppedDataTransferAsAction(
AutoEditActionDataSetter& aEditActionData,
dom::DataTransfer& aDataTransfer, const EditorDOMPoint& aDroppedAt,
nsIPrincipal* aSourcePrincipal) final;
AutoEditActionDataSetter& aEditActionData, DataTransfer& aDataTransfer,
const EditorDOMPoint& aDroppedAt, nsIPrincipal* aSourcePrincipal) final;
/**
* GetInlineStyles() retrieves the style of aElement and modifies each item of
@@ -3135,10 +3138,12 @@ class HTMLEditor final : public EditorBase,
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandlePaste(AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) final;
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandlePasteAsQuotation(AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) final;
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandlePasteTransferable(AutoEditActionDataSetter& aEditActionData,
nsITransferable& aTransferable) final;
@@ -3152,8 +3157,9 @@ class HTMLEditor final : public EditorBase,
* nsIClipboard::kSelectionClipboard.
* @param aEditingHost The editing host.
*/
MOZ_CAN_RUN_SCRIPT nsresult PasteInternal(
nsIClipboard::ClipboardType aClipboardType, const Element& aEditingHost);
MOZ_CAN_RUN_SCRIPT nsresult
PasteInternal(nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer, const Element& aEditingHost);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) final;
@@ -3433,7 +3439,7 @@ class HTMLEditor final : public EditorBase,
RefPtr<dom::BlobImpl> mBlob;
RefPtr<HTMLEditor> mHTMLEditor;
RefPtr<const Element> mEditingHost;
RefPtr<dom::DataTransfer> mDataTransfer;
RefPtr<DataTransfer> mDataTransfer;
EditorDOMPoint mPointToInsert;
EditAction mEditAction;
SafeToInsertData mSafeToInsertData;
@@ -3735,7 +3741,8 @@ class HTMLEditor final : public EditorBase,
// Methods for handling plaintext quotations
MOZ_CAN_RUN_SCRIPT nsresult PasteAsPlaintextQuotation(
nsIClipboard::ClipboardType aSelectionType, const Element& aEditingHost);
nsIClipboard::ClipboardType aSelectionType, DataTransfer* aDataTransfer,
const Element& aEditingHost);
enum class AddCites { No, Yes };
/**
@@ -3781,13 +3788,13 @@ class HTMLEditor final : public EditorBase,
* @param aIndex index of aDataTransfer's item to insert.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertFromDataTransfer(
const dom::DataTransfer* aDataTransfer, uint32_t aIndex,
const DataTransfer* aDataTransfer, uint32_t aIndex,
nsIPrincipal* aSourcePrincipal, const EditorDOMPoint& aDroppedAt,
DeleteSelectedContent aDeleteSelectedContent,
const Element& aEditingHost);
static HavePrivateHTMLFlavor ClipboardHasPrivateHTMLFlavor(
nsIClipboard* clipboard);
static HavePrivateHTMLFlavor DataTransferOrClipboardHasPrivateHTMLFlavor(
DataTransfer* aDataTransfer, nsIClipboard* clipboard);
/**
* CF_HTML:

View File

@@ -127,10 +127,11 @@ nsresult PasteNoFormattingCommand::DoCommand(Command aCommand,
return NS_ERROR_FAILURE;
}
// Known live because we hold a ref above in "editor"
nsresult rv = MOZ_KnownLive(htmlEditor)
->PasteNoFormattingAsAction(
nsIClipboard::kGlobalClipboard,
EditorBase::DispatchPasteEvent::Yes, aPrincipal);
nsresult rv =
MOZ_KnownLive(htmlEditor)
->PasteNoFormattingAsAction(nsIClipboard::kGlobalClipboard,
EditorBase::DispatchPasteEvent::Yes,
nullptr, aPrincipal);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"HTMLEditor::PasteNoFormattingAsAction(DispatchPasteEvent::Yes) failed");

View File

@@ -1486,6 +1486,8 @@ void HTMLEditor::HTMLTransferablePreparer::AddDataFlavorsInBestOrder(
// Create the desired DataFlavor for the type of data
// we want to get out of the transferable
// This should only happen in html editors, not plaintext
// Note that if you add more flavors here you will need to add them
// to DataTransfer::GetExternalClipboardFormats as well.
if (!mHTMLEditor.IsPlaintextMailComposer() &&
!(mEditingHost && mEditingHost->IsContentEditablePlainTextOnly())) {
DebugOnly<nsresult> rvIgnored =
@@ -2346,8 +2348,15 @@ nsresult HTMLEditor::InsertFromDataTransfer(
}
// static
HTMLEditor::HavePrivateHTMLFlavor HTMLEditor::ClipboardHasPrivateHTMLFlavor(
nsIClipboard* aClipboard) {
HTMLEditor::HavePrivateHTMLFlavor
HTMLEditor::DataTransferOrClipboardHasPrivateHTMLFlavor(
DataTransfer* aDataTransfer, nsIClipboard* aClipboard) {
nsresult rv;
if (aDataTransfer) {
return aDataTransfer->HasPrivateHTMLFlavor() ? HavePrivateHTMLFlavor::Yes
: HavePrivateHTMLFlavor::No;
}
// otherwise, fall back to clipboard
if (NS_WARN_IF(!aClipboard)) {
return HavePrivateHTMLFlavor::No;
}
@@ -2356,7 +2365,7 @@ HTMLEditor::HavePrivateHTMLFlavor HTMLEditor::ClipboardHasPrivateHTMLFlavor(
// we know we have our own internal html format on clipboard.
bool hasPrivateHTMLFlavor = false;
AutoTArray<nsCString, 1> flavArray = {nsDependentCString(kHTMLContext)};
nsresult rv = aClipboard->HasDataMatchingFlavors(
rv = aClipboard->HasDataMatchingFlavors(
flavArray, nsIClipboard::kGlobalClipboard, &hasPrivateHTMLFlavor);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"nsIClipboard::HasDataMatchingFlavors(nsIClipboard::"
@@ -2366,9 +2375,10 @@ HTMLEditor::HavePrivateHTMLFlavor HTMLEditor::ClipboardHasPrivateHTMLFlavor(
}
nsresult HTMLEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) {
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) {
aEditActionData.InitializeDataTransferWithClipboard(
SettingDataTransfer::eWithFormat, aClipboardType);
SettingDataTransfer::eWithFormat, aDataTransfer, aClipboardType);
nsresult rv = aEditActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
if (NS_FAILED(rv)) {
NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
@@ -2380,12 +2390,13 @@ nsresult HTMLEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData,
if (NS_WARN_IF(!editingHost)) {
return NS_ERROR_FAILURE;
}
rv = PasteInternal(aClipboardType, *editingHost);
rv = PasteInternal(aClipboardType, aDataTransfer, *editingHost);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::PasteInternal() failed");
return rv;
}
nsresult HTMLEditor::PasteInternal(nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer,
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
@@ -2414,14 +2425,10 @@ nsresult HTMLEditor::PasteInternal(nsIClipboard::ClipboardType aClipboardType,
return NS_ERROR_FAILURE;
}
// Get the Data from the clipboard
auto* windowContext = GetDocument()->GetWindowContext();
if (!windowContext) {
NS_WARNING("No window context");
return NS_ERROR_FAILURE;
}
rv = clipboard->GetData(transferable, aClipboardType, windowContext);
rv = GetDataFromDataTransferOrClipboard(aDataTransfer, transferable,
aClipboardType);
if (NS_FAILED(rv)) {
NS_WARNING("nsIClipboard::GetData() failed");
NS_WARNING("EditorBase::GetDataFromDataTransferOrClipboard() failed");
return rv;
}
@@ -2431,7 +2438,7 @@ nsresult HTMLEditor::PasteInternal(nsIClipboard::ClipboardType aClipboardType,
// If we have our internal html flavor on the clipboard, there is special
// context to use instead of cfhtml context.
const HavePrivateHTMLFlavor clipboardHasPrivateHTMLFlavor =
ClipboardHasPrivateHTMLFlavor(clipboard);
DataTransferOrClipboardHasPrivateHTMLFlavor(aDataTransfer, clipboard);
if (clipboardHasPrivateHTMLFlavor == HavePrivateHTMLFlavor::Yes) {
nsCOMPtr<nsITransferable> contextTransferable =
do_CreateInstance("@mozilla.org/widget/transferable;1");
@@ -2448,10 +2455,8 @@ nsresult HTMLEditor::PasteInternal(nsIClipboard::ClipboardType aClipboardType,
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsITransferable::AddDataFlavor(kHTMLContext) failed, but ignored");
rvIgnored =
clipboard->GetData(contextTransferable, aClipboardType, windowContext);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"nsIClipboard::GetData() failed, but ignored");
GetDataFromDataTransferOrClipboard(aDataTransfer, contextTransferable,
aClipboardType);
nsCOMPtr<nsISupports> contextDataObj;
rv = contextTransferable->GetTransferData(kHTMLContext,
getter_AddRefs(contextDataObj));
@@ -2480,10 +2485,8 @@ nsresult HTMLEditor::PasteInternal(nsIClipboard::ClipboardType aClipboardType,
NS_SUCCEEDED(rvIgnored),
"nsITransferable::AddDataFlavor(kHTMLInfo) failed, but ignored");
rvIgnored =
clipboard->GetData(infoTransferable, aClipboardType, windowContext);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"nsIClipboard::GetData() failed, but ignored");
GetDataFromDataTransferOrClipboard(aDataTransfer, infoTransferable,
aClipboardType);
nsCOMPtr<nsISupports> infoDataObj;
rv = infoTransferable->GetTransferData(kHTMLInfo,
getter_AddRefs(infoDataObj));
@@ -2551,10 +2554,28 @@ nsresult HTMLEditor::HandlePasteTransferable(
nsresult HTMLEditor::PasteNoFormattingAsAction(
nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent, nsIPrincipal* aPrincipal) {
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer /* = nullptr */,
nsIPrincipal* aPrincipal /* = nullptr */) {
if (IsReadonly()) {
return NS_OK;
}
// Create the same DataTransfer object here so we can share it between
// the clipboard event and its data with the call to
// InsertFromTransferableWithSelection below. This prevents
// race conditions with Content Analysis on like we see in bug 1918027.
RefPtr<DataTransfer> dataTransfer =
aDataTransfer ? RefPtr<DataTransfer>(aDataTransfer)
: RefPtr<DataTransfer>(CreateDataTransferForPaste(
ePasteNoFormatting, aClipboardType));
auto clearDataTransfer = MakeScopeExit([&] {
// If the caller passed in aDataTransfer, they are responsible for clearing
// this.
if (!aDataTransfer && dataTransfer) {
dataTransfer->ClearForPaste();
}
});
AutoEditActionDataSetter editActionData(*this, EditAction::ePaste,
aPrincipal);
@@ -2562,7 +2583,7 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(
return NS_ERROR_NOT_INITIALIZED;
}
editActionData.InitializeDataTransferWithClipboard(
SettingDataTransfer::eWithoutFormat, aClipboardType);
SettingDataTransfer::eWithoutFormat, dataTransfer, aClipboardType);
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
@@ -2571,14 +2592,22 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(
}
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
Result<ClipboardEventResult, nsresult> ret =
DispatchClipboardEventAndUpdateClipboard(ePasteNoFormatting,
Some(aClipboardType));
if (MOZ_UNLIKELY(ret.isErr())) {
NS_WARNING(
"EditorBase::DispatchClipboardEventAndUpdateClipboard("
"ePasteNoFormatting) failed");
return EditorBase::ToGenericNSResult(ret.unwrapErr());
Result<ClipboardEventResult, nsresult> ret = Err(NS_ERROR_FAILURE);
{
// This method is not set up to pass back the new aDataTransfer
// if it changes. If we need this in the future, we can change
// aDataTransfer to be a RefPtr<DataTransfer>*.
MOZ_ASSERT(!aDataTransfer);
AutoTrackDataTransferForPaste trackDataTransfer(*this, dataTransfer);
ret = DispatchClipboardEventAndUpdateClipboard(
ePasteNoFormatting, Some(aClipboardType), dataTransfer);
if (MOZ_UNLIKELY(ret.isErr())) {
NS_WARNING(
"EditorBase::DispatchClipboardEventAndUpdateClipboard("
"ePasteNoFormatting) failed");
return EditorBase::ToGenericNSResult(ret.unwrapErr());
}
}
switch (ret.inspect()) {
case ClipboardEventResult::DoDefault:
@@ -2607,17 +2636,17 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(
}
if (editorBase != this) {
if (editorBase->IsHTMLEditor()) {
nsresult rv =
MOZ_KnownLive(editorBase->AsHTMLEditor())
->PasteNoFormattingAsAction(
aClipboardType, DispatchPasteEvent::No, aPrincipal);
nsresult rv = MOZ_KnownLive(editorBase->AsHTMLEditor())
->PasteNoFormattingAsAction(
aClipboardType, DispatchPasteEvent::No,
dataTransfer, aPrincipal);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::PasteNoFormattingAsAction("
"DispatchPasteEvent::No) failed");
return EditorBase::ToGenericNSResult(rv);
}
nsresult rv = editorBase->PasteAsAction(
aClipboardType, DispatchPasteEvent::No, aPrincipal);
aClipboardType, DispatchPasteEvent::No, dataTransfer, aPrincipal);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::PasteAsAction(DispatchPasteEvent::No) failed");
@@ -2652,24 +2681,6 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(
return NS_OK;
}
// Get Clipboard Service
nsCOMPtr<nsIClipboard> clipboard(
do_GetService("@mozilla.org/widget/clipboard;1", &rv));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get nsIClipboard service");
return rv;
}
if (!GetDocument()) {
NS_WARNING("Editor didn't have document, but ignored");
return NS_OK;
}
auto* windowContext = GetDocument()->GetWindowContext();
if (!windowContext) {
NS_WARNING("Editor didn't have document window context, but ignored");
return NS_OK;
}
Result<nsCOMPtr<nsITransferable>, nsresult> maybeTransferable =
EditorUtils::CreateTransferableForPlainText(*GetDocument());
if (maybeTransferable.isErr()) {
@@ -2683,11 +2694,10 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(
"ignored");
return NS_OK;
}
// Get the Data from the clipboard
rv = clipboard->GetData(transferable, aClipboardType, windowContext);
rv = GetDataFromDataTransferOrClipboard(dataTransfer, transferable,
aClipboardType);
if (NS_FAILED(rv)) {
NS_WARNING("nsIClipboard::GetData() failed");
NS_WARNING("EditorBase::GetDataFromDataTransferOrClipboard() failed");
return rv;
}
@@ -2798,11 +2808,11 @@ bool HTMLEditor::CanPasteTransferable(nsITransferable* aTransferable) {
nsresult HTMLEditor::HandlePasteAsQuotation(
AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) {
nsIClipboard::ClipboardType aClipboardType, DataTransfer* aDataTransfer) {
MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
aClipboardType == nsIClipboard::kSelectionClipboard);
aEditActionData.InitializeDataTransferWithClipboard(
SettingDataTransfer::eWithFormat, aClipboardType);
SettingDataTransfer::eWithFormat, aDataTransfer, aClipboardType);
if (NS_WARN_IF(!aEditActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
@@ -2822,7 +2832,8 @@ nsresult HTMLEditor::HandlePasteAsQuotation(
if (IsPlaintextMailComposer() ||
editingHost->IsContentEditablePlainTextOnly()) {
nsresult rv = PasteAsPlaintextQuotation(aClipboardType, *editingHost);
nsresult rv =
PasteAsPlaintextQuotation(aClipboardType, aDataTransfer, *editingHost);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::PasteAsPlaintextQuotation() failed");
return rv;
@@ -2923,22 +2934,15 @@ nsresult HTMLEditor::HandlePasteAsQuotation(
return rv;
}
rv = PasteInternal(aClipboardType, *editingHost);
rv = PasteInternal(aClipboardType, aDataTransfer, *editingHost);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::PasteInternal() failed");
return rv;
}
nsresult HTMLEditor::PasteAsPlaintextQuotation(
nsIClipboard::ClipboardType aSelectionType, const Element& aEditingHost) {
// Get Clipboard Service
nsIClipboard::ClipboardType aSelectionType, DataTransfer* aDataTransfer,
const Element& aEditingHost) {
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard =
do_GetService("@mozilla.org/widget/clipboard;1", &rv);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get nsIClipboard service");
return rv;
}
// Create generic Transferable for getting the data
nsCOMPtr<nsITransferable> transferable =
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
@@ -2970,9 +2974,8 @@ nsresult HTMLEditor::PasteAsPlaintextQuotation(
"nsITransferable::AddDataFlavor(kTextMime) failed, but ignored");
// Get the Data from the clipboard
rvIgnored = clipboard->GetData(transferable, aSelectionType, windowContext);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"nsIClipboard::GetData() failed, but ignored");
GetDataFromDataTransferOrClipboard(aDataTransfer, transferable,
aSelectionType);
// Now we ask the transferable for the data
// it still owns the data, we just have a pointer to it.

View File

@@ -565,22 +565,13 @@ bool TextEditor::IsCopyToClipboardAllowedInternal() const {
nsresult TextEditor::HandlePasteAsQuotation(
AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) {
nsIClipboard::ClipboardType aClipboardType, DataTransfer* aDataTransfer) {
MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
aClipboardType == nsIClipboard::kSelectionClipboard);
if (NS_WARN_IF(!GetDocument())) {
return NS_OK;
}
// Get Clipboard Service
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard =
do_GetService("@mozilla.org/widget/clipboard;1", &rv);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get nsIClipboard service");
return rv;
}
// XXX Why don't we dispatch ePaste event here?
// Get the nsITransferable interface for getting the data from the clipboard
@@ -598,13 +589,9 @@ nsresult TextEditor::HandlePasteAsQuotation(
return NS_OK;
}
auto* windowContext = GetDocument()->GetWindowContext();
if (!windowContext) {
NS_WARNING("Editor didn't have document window context");
return NS_ERROR_FAILURE;
}
// Get the Data from the clipboard
rv = clipboard->GetData(trans, aClipboardType, windowContext);
nsresult rv =
GetDataFromDataTransferOrClipboard(aDataTransfer, trans, aClipboardType);
// Now we ask the transferable for the data
// it still owns the data, we just have a pointer to it.

View File

@@ -465,9 +465,8 @@ class TextEditor final : public EditorBase,
SelectionHandling aSelectionHandling) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertDroppedDataTransferAsAction(
AutoEditActionDataSetter& aEditActionData,
dom::DataTransfer& aDataTransfer, const EditorDOMPoint& aDroppedAt,
nsIPrincipal* aSourcePrincipal) final;
AutoEditActionDataSetter& aEditActionData, DataTransfer& aDataTransfer,
const EditorDOMPoint& aDroppedAt, nsIPrincipal* aSourcePrincipal) final;
/**
* HandleDeleteSelectionInternal() is a helper method of
@@ -568,10 +567,12 @@ class TextEditor final : public EditorBase,
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandlePaste(AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) final;
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandlePasteAsQuotation(AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) final;
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandlePasteTransferable(AutoEditActionDataSetter& aEditActionData,
nsITransferable& aTransferable) final;

View File

@@ -11,6 +11,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
@@ -161,7 +162,8 @@ nsresult TextEditor::InsertDroppedDataTransferAsAction(
}
nsresult TextEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData,
nsIClipboard::ClipboardType aClipboardType) {
nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer) {
if (NS_WARN_IF(!GetDocument())) {
return NS_OK;
}
@@ -193,17 +195,13 @@ nsresult TextEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData,
return NS_OK; // XXX Why?
}
// Get the Data from the clipboard.
auto* windowContext = GetDocument()->GetWindowContext();
if (!windowContext) {
NS_WARNING("Editor didn't have document window context");
return NS_ERROR_FAILURE;
}
rv = clipboard->GetData(transferable, aClipboardType, windowContext);
rv = GetDataFromDataTransferOrClipboard(aDataTransfer, transferable,
aClipboardType);
if (NS_FAILED(rv)) {
NS_WARNING("nsIClipboard::GetData() failed, but ignored");
return NS_OK; // XXX Why?
NS_WARNING("EditorBase::GetDataFromDataTransferOrClipboard() failed");
return rv;
}
// XXX Why don't we check this first?
if (!IsModifiable()) {
return NS_OK;

View File

@@ -2430,7 +2430,7 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::SelectAll() {
NS_IMETHODIMP nsDocumentViewer::CopySelection() {
RefPtr<PresShell> presShell = mPresShell;
nsCopySupport::FireClipboardEvent(eCopy, Some(nsIClipboard::kGlobalClipboard),
presShell, nullptr);
presShell, nullptr, nullptr);
return NS_OK;
}