LCOV - code coverage report
Current view: top level - editor/libeditor - TextEditorDataTransfer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 204 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13