editor/libeditor/text/nsPlaintextDataTransfer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     7 #include "mozilla/MouseEvents.h"
     8 #include "nsAString.h"
     9 #include "nsCOMPtr.h"
    10 #include "nsCRT.h"
    11 #include "nsComponentManagerUtils.h"
    12 #include "nsContentUtils.h"
    13 #include "nsDebug.h"
    14 #include "nsEditor.h"
    15 #include "nsEditorUtils.h"
    16 #include "nsError.h"
    17 #include "nsIClipboard.h"
    18 #include "nsIContent.h"
    19 #include "nsIDOMDataTransfer.h"
    20 #include "nsIDOMDocument.h"
    21 #include "nsIDOMDragEvent.h"
    22 #include "nsIDOMEvent.h"
    23 #include "nsIDOMNode.h"
    24 #include "nsIDOMRange.h"
    25 #include "nsIDOMUIEvent.h"
    26 #include "nsIDocument.h"
    27 #include "nsIDragService.h"
    28 #include "nsIDragSession.h"
    29 #include "nsIEditor.h"
    30 #include "nsIEditorIMESupport.h"
    31 #include "nsIDocShell.h"
    32 #include "nsIDocShellTreeItem.h"
    33 #include "nsIPrincipal.h"
    34 #include "nsIFormControl.h"
    35 #include "nsIPlaintextEditor.h"
    36 #include "nsISelection.h"
    37 #include "nsISupportsPrimitives.h"
    38 #include "nsITransferable.h"
    39 #include "nsIVariant.h"
    40 #include "nsLiteralString.h"
    41 #include "nsPlaintextEditor.h"
    42 #include "nsSelectionState.h"
    43 #include "nsServiceManagerUtils.h"
    44 #include "nsString.h"
    45 #include "nsXPCOM.h"
    46 #include "nscore.h"
    48 class nsILoadContext;
    49 class nsISupports;
    51 using namespace mozilla;
    52 using namespace mozilla::dom;
    54 NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
    55 {
    56   // Create generic Transferable for getting the data
    57   nsresult rv = CallCreateInstance("@mozilla.org/widget/transferable;1", transferable);
    58   NS_ENSURE_SUCCESS(rv, rv);
    60   // Get the nsITransferable interface for getting the data from the clipboard
    61   if (transferable) {
    62     nsCOMPtr<nsIDocument> destdoc = GetDocument();
    63     nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
    64     (*transferable)->Init(loadContext);
    66     (*transferable)->AddDataFlavor(kUnicodeMime);
    67     (*transferable)->AddDataFlavor(kMozTextInternal);
    68   };
    69   return NS_OK;
    70 }
    72 nsresult nsPlaintextEditor::InsertTextAt(const nsAString &aStringToInsert,
    73                                          nsIDOMNode *aDestinationNode,
    74                                          int32_t aDestOffset,
    75                                          bool aDoDeleteSelection)
    76 {
    77   if (aDestinationNode)
    78   {
    79     nsresult res;
    80     nsCOMPtr<nsISelection>selection;
    81     res = GetSelection(getter_AddRefs(selection));
    82     NS_ENSURE_SUCCESS(res, res);
    84     nsCOMPtr<nsIDOMNode> targetNode = aDestinationNode;
    85     int32_t targetOffset = aDestOffset;
    87     if (aDoDeleteSelection)
    88     {
    89       // Use an auto tracker so that our drop point is correctly
    90       // positioned after the delete.
    91       nsAutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
    92       res = DeleteSelection(eNone, eStrip);
    93       NS_ENSURE_SUCCESS(res, res);
    94     }
    96     res = selection->Collapse(targetNode, targetOffset);
    97     NS_ENSURE_SUCCESS(res, res);
    98   }
   100   return InsertText(aStringToInsert);
   101 }
   103 NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *aTransferable,
   104                                                             nsIDOMNode *aDestinationNode,
   105                                                             int32_t aDestOffset,
   106                                                             bool aDoDeleteSelection)
   107 {
   108   nsresult rv = NS_OK;
   109   char* bestFlavor = nullptr;
   110   nsCOMPtr<nsISupports> genericDataObj;
   111   uint32_t len = 0;
   112   if (NS_SUCCEEDED(aTransferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len))
   113       && bestFlavor && (0 == nsCRT::strcmp(bestFlavor, kUnicodeMime) ||
   114                         0 == nsCRT::strcmp(bestFlavor, kMozTextInternal)))
   115   {
   116     nsAutoTxnsConserveSelection dontSpazMySelection(this);
   117     nsCOMPtr<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
   118     if (textDataObj && len > 0)
   119     {
   120       nsAutoString stuffToPaste;
   121       textDataObj->GetData(stuffToPaste);
   122       NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
   124       // Sanitize possible carriage returns in the string to be inserted
   125       nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste);
   127       nsAutoEditBatch beginBatching(this);
   128       rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection);
   129     }
   130   }
   131   NS_Free(bestFlavor);
   133   // Try to scroll the selection into view if the paste/drop succeeded
   135   if (NS_SUCCEEDED(rv))
   136     ScrollSelectionIntoView(false);
   138   return rv;
   139 }
   141 nsresult nsPlaintextEditor::InsertFromDataTransfer(DataTransfer *aDataTransfer,
   142                                                    int32_t aIndex,
   143                                                    nsIDOMDocument *aSourceDoc,
   144                                                    nsIDOMNode *aDestinationNode,
   145                                                    int32_t aDestOffset,
   146                                                    bool aDoDeleteSelection)
   147 {
   148   nsCOMPtr<nsIVariant> data;
   149   aDataTransfer->MozGetDataAt(NS_LITERAL_STRING("text/plain"), aIndex,
   150                               getter_AddRefs(data));
   151   if (data) {
   152     nsAutoString insertText;
   153     data->GetAsAString(insertText);
   154     nsContentUtils::PlatformToDOMLineBreaks(insertText);
   156     nsAutoEditBatch beginBatching(this);
   157     return InsertTextAt(insertText, aDestinationNode, aDestOffset, aDoDeleteSelection);
   158   }
   160   return NS_OK;
   161 }
   163 nsresult nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
   164 {
   165   ForceCompositionEnd();
   167   nsCOMPtr<nsIDOMDragEvent> dragEvent(do_QueryInterface(aDropEvent));
   168   NS_ENSURE_TRUE(dragEvent, NS_ERROR_FAILURE);
   170   nsCOMPtr<nsIDOMDataTransfer> domDataTransfer;
   171   dragEvent->GetDataTransfer(getter_AddRefs(domDataTransfer));
   172   nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer);
   173   NS_ENSURE_TRUE(dataTransfer, NS_ERROR_FAILURE);
   175   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
   176   NS_ASSERTION(dragSession, "No drag session");
   178   nsCOMPtr<nsIDOMNode> sourceNode;
   179   dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode));
   181   nsCOMPtr<nsIDOMDocument> srcdomdoc;
   182   if (sourceNode) {
   183     sourceNode->GetOwnerDocument(getter_AddRefs(srcdomdoc));
   184     NS_ENSURE_TRUE(sourceNode, NS_ERROR_FAILURE);
   185   }
   187   if (nsContentUtils::CheckForSubFrameDrop(dragSession,
   188         aDropEvent->GetInternalNSEvent()->AsDragEvent())) {
   189     // Don't allow drags from subframe documents with different origins than
   190     // the drop destination.
   191     if (srcdomdoc && !IsSafeToInsertData(srcdomdoc))
   192       return NS_OK;
   193   }
   195   // Current doc is destination
   196   nsCOMPtr<nsIDOMDocument> destdomdoc = GetDOMDocument();
   197   NS_ENSURE_TRUE(destdomdoc, NS_ERROR_NOT_INITIALIZED);
   199   uint32_t numItems = 0;
   200   nsresult rv = dataTransfer->GetMozItemCount(&numItems);
   201   NS_ENSURE_SUCCESS(rv, rv);
   202   if (numItems < 1) return NS_ERROR_FAILURE;  // nothing to drop?
   204   // Combine any deletion and drop insertion into one transaction
   205   nsAutoEditBatch beginBatching(this);
   207   bool deleteSelection = false;
   209   // We have to figure out whether to delete and relocate caret only once
   210   // Parent and offset are under the mouse cursor
   211   nsCOMPtr<nsIDOMUIEvent> uiEvent = do_QueryInterface(aDropEvent);
   212   NS_ENSURE_TRUE(uiEvent, NS_ERROR_FAILURE);
   214   nsCOMPtr<nsIDOMNode> newSelectionParent;
   215   rv = uiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
   216   NS_ENSURE_SUCCESS(rv, rv);
   217   NS_ENSURE_TRUE(newSelectionParent, NS_ERROR_FAILURE);
   219   int32_t newSelectionOffset;
   220   rv = uiEvent->GetRangeOffset(&newSelectionOffset);
   221   NS_ENSURE_SUCCESS(rv, rv);
   223   nsCOMPtr<nsISelection> selection;
   224   rv = GetSelection(getter_AddRefs(selection));
   225   NS_ENSURE_SUCCESS(rv, rv);
   226   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
   228   bool isCollapsed = selection->Collapsed();
   230   // Only the nsHTMLEditor::FindUserSelectAllNode returns a node.
   231   nsCOMPtr<nsIDOMNode> userSelectNode = FindUserSelectAllNode(newSelectionParent);
   232   if (userSelectNode)
   233   {
   234     // The drop is happening over a "-moz-user-select: all"
   235     // subtree so make sure the content we insert goes before
   236     // the root of the subtree.
   237     //
   238     // XXX: Note that inserting before the subtree matches the
   239     //      current behavior when dropping on top of an image.
   240     //      The decision for dropping before or after the
   241     //      subtree should really be done based on coordinates.
   243     newSelectionParent = GetNodeLocation(userSelectNode, &newSelectionOffset);
   245     NS_ENSURE_TRUE(newSelectionParent, NS_ERROR_FAILURE);
   246   }
   248   // Check if mouse is in the selection
   249   // if so, jump through some hoops to determine if mouse is over selection (bail)
   250   // and whether user wants to copy selection or delete it
   251   if (!isCollapsed)
   252   {
   253     // We never have to delete if selection is already collapsed
   254     bool cursorIsInSelection = false;
   256     int32_t rangeCount;
   257     rv = selection->GetRangeCount(&rangeCount);
   258     NS_ENSURE_SUCCESS(rv, rv);
   260     for (int32_t j = 0; j < rangeCount; j++)
   261     {
   262       nsCOMPtr<nsIDOMRange> range;
   263       rv = selection->GetRangeAt(j, getter_AddRefs(range));
   264       if (NS_FAILED(rv) || !range) 
   265         continue;  // don't bail yet, iterate through them all
   267       rv = range->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
   268       if (cursorIsInSelection)
   269         break;
   270     }
   272     if (cursorIsInSelection)
   273     {
   274       // Dragging within same doc can't drop on itself -- leave!
   275       if (srcdomdoc == destdomdoc)
   276         return NS_OK;
   278       // Dragging from another window onto a selection
   279       // XXX Decision made to NOT do this,
   280       //     note that 4.x does replace if dropped on
   281       //deleteSelection = true;
   282     }
   283     else 
   284     {
   285       // We are NOT over the selection
   286       if (srcdomdoc == destdomdoc)
   287       {
   288         // Within the same doc: delete if user doesn't want to copy
   289         uint32_t dropEffect;
   290         dataTransfer->GetDropEffectInt(&dropEffect);
   291         deleteSelection = !(dropEffect & nsIDragService::DRAGDROP_ACTION_COPY);
   292       }
   293       else
   294       {
   295         // Different source doc: Don't delete
   296         deleteSelection = false;
   297       }
   298     }
   299   }
   301   if (IsPlaintextEditor()) {
   302     nsCOMPtr<nsIContent> content = do_QueryInterface(newSelectionParent);
   303     while (content) {
   304       nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
   305       if (formControl && !formControl->AllowDrop()) {
   306         // Don't allow dropping into a form control that doesn't allow being
   307         // dropped into.
   308         return NS_OK;
   309       }
   310       content = content->GetParent();
   311     }
   312   }
   314   for (uint32_t i = 0; i < numItems; ++i) {
   315     InsertFromDataTransfer(dataTransfer, i, srcdomdoc, newSelectionParent,
   316                            newSelectionOffset, deleteSelection);
   317   }
   319   if (NS_SUCCEEDED(rv))
   320     ScrollSelectionIntoView(false);
   322   return rv;
   323 }
   325 NS_IMETHODIMP nsPlaintextEditor::Paste(int32_t aSelectionType)
   326 {
   327   if (!FireClipboardEvent(NS_PASTE, aSelectionType))
   328     return NS_OK;
   330   // Get Clipboard Service
   331   nsresult rv;
   332   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   333   if ( NS_FAILED(rv) )
   334     return rv;
   336   // Get the nsITransferable interface for getting the data from the clipboard
   337   nsCOMPtr<nsITransferable> trans;
   338   rv = PrepareTransferable(getter_AddRefs(trans));
   339   if (NS_SUCCEEDED(rv) && trans)
   340   {
   341     // Get the Data from the clipboard  
   342     if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
   343     {
   344       // handle transferable hooks
   345       nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   346       if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, trans))
   347         return NS_OK;
   349       rv = InsertTextFromTransferable(trans, nullptr, 0, true);
   350     }
   351   }
   353   return rv;
   354 }
   356 NS_IMETHODIMP nsPlaintextEditor::PasteTransferable(nsITransferable *aTransferable)
   357 {
   358   // Use an invalid value for the clipboard type as data comes from aTransferable
   359   // and we don't currently implement a way to put that in the data transfer yet.
   360   if (!FireClipboardEvent(NS_PASTE, -1))
   361     return NS_OK;
   363   if (!IsModifiable())
   364     return NS_OK;
   366   // handle transferable hooks
   367   nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   368   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, aTransferable))
   369     return NS_OK;
   371   return InsertTextFromTransferable(aTransferable, nullptr, 0, true);
   372 }
   374 NS_IMETHODIMP nsPlaintextEditor::CanPaste(int32_t aSelectionType, bool *aCanPaste)
   375 {
   376   NS_ENSURE_ARG_POINTER(aCanPaste);
   377   *aCanPaste = false;
   379   // can't paste if readonly
   380   if (!IsModifiable())
   381     return NS_OK;
   383   nsresult rv;
   384   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   385   NS_ENSURE_SUCCESS(rv, rv);
   387   // the flavors that we can deal with
   388   const char* textEditorFlavors[] = { kUnicodeMime };
   390   bool haveFlavors;
   391   rv = clipboard->HasDataMatchingFlavors(textEditorFlavors,
   392                                          ArrayLength(textEditorFlavors),
   393                                          aSelectionType, &haveFlavors);
   394   NS_ENSURE_SUCCESS(rv, rv);
   396   *aCanPaste = haveFlavors;
   397   return NS_OK;
   398 }
   401 NS_IMETHODIMP nsPlaintextEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste)
   402 {
   403   NS_ENSURE_ARG_POINTER(aCanPaste);
   405   // can't paste if readonly
   406   if (!IsModifiable()) {
   407     *aCanPaste = false;
   408     return NS_OK;
   409   }
   411   // If |aTransferable| is null, assume that a paste will succeed.
   412   if (!aTransferable) {
   413     *aCanPaste = true;
   414     return NS_OK;
   415   }
   417   nsCOMPtr<nsISupports> data;
   418   uint32_t dataLen;
   419   nsresult rv = aTransferable->GetTransferData(kUnicodeMime,
   420                                                getter_AddRefs(data),
   421                                                &dataLen);
   422   if (NS_SUCCEEDED(rv) && data)
   423     *aCanPaste = true;
   424   else
   425     *aCanPaste = false;
   427   return NS_OK;
   428 }
   430 bool nsPlaintextEditor::IsSafeToInsertData(nsIDOMDocument* aSourceDoc)
   431 {
   432   // Try to determine whether we should use a sanitizing fragment sink
   433   bool isSafe = false;
   435   nsCOMPtr<nsIDocument> destdoc = GetDocument();
   436   NS_ASSERTION(destdoc, "Where is our destination doc?");
   437   nsCOMPtr<nsIDocShellTreeItem> dsti = destdoc->GetDocShell();
   438   nsCOMPtr<nsIDocShellTreeItem> root;
   439   if (dsti)
   440     dsti->GetRootTreeItem(getter_AddRefs(root));
   441   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(root);
   442   uint32_t appType;
   443   if (docShell && NS_SUCCEEDED(docShell->GetAppType(&appType)))
   444     isSafe = appType == nsIDocShell::APP_TYPE_EDITOR;
   445   if (!isSafe && aSourceDoc) {
   446     nsCOMPtr<nsIDocument> srcdoc = do_QueryInterface(aSourceDoc);
   447     NS_ASSERTION(srcdoc, "Where is our source doc?");
   449     nsIPrincipal* srcPrincipal = srcdoc->NodePrincipal();
   450     nsIPrincipal* destPrincipal = destdoc->NodePrincipal();
   451     NS_ASSERTION(srcPrincipal && destPrincipal, "How come we don't have a principal?");
   452     srcPrincipal->Subsumes(destPrincipal, &isSafe);
   453   }
   455   return isSafe;
   456 }

mercurial