/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsICaret.h" #include "nsPlaintextEditor.h" #include "nsTextEditUtils.h" #include "nsIDOMText.h" #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" #include "nsIDOMAttr.h" #include "nsIDocument.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMNSEvent.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMKeyListener.h" #include "nsIDOMMouseListener.h" #include "nsIDOMMouseEvent.h" #include "nsISelection.h" #include "nsISelectionPrivate.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" #include "nsISelectionController.h" #include "nsIFrameSelection.h" // For TABLESELECTION_ defines #include "nsICSSLoader.h" #include "nsICSSStyleSheet.h" #include "nsIHTMLContentContainer.h" #include "nsIStyleSet.h" #include "nsIDocumentObserver.h" #include "nsIDocumentStateListener.h" #include "nsIEnumerator.h" #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsEditorCID.h" #include "nsLayoutCID.h" #include "nsIDOMRange.h" #include "nsIDOMNSRange.h" #include "nsISupportsArray.h" #include "nsVoidArray.h" #include "nsIURL.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsWidgetsCID.h" #include "nsIDocumentEncoder.h" #include "nsIDOMDocumentFragment.h" #include "nsIPresShell.h" #include "nsIPresContext.h" #include "nsIImage.h" #include "nsAOLCiter.h" #include "nsInternetCiter.h" #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" // netwerk #include "nsIURI.h" #include "nsNetUtil.h" // Drag & Drop, Clipboard #include "nsWidgetsCID.h" #include "nsIClipboard.h" #include "nsITransferable.h" #include "nsIDragService.h" #include "nsIDOMNSUIEvent.h" #include "nsIDocShell.h" #include "nsIClipboardDragDropHooks.h" #include "nsIClipboardDragDropHookList.h" #include "nsISimpleEnumerator.h" // Misc #include "nsEditorUtils.h" #include "nsIPref.h" const PRUnichar nbsp = 160; // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); // private clipboard data flavors for html copy/paste #define kHTMLContext "text/_moz_htmlcontext" #define kHTMLInfo "text/_moz_htmlinfo" #if defined(NS_DEBUG) && defined(DEBUG_buster) static PRBool gNoisy = PR_FALSE; #else static const PRBool gNoisy = PR_FALSE; #endif NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable) { // Create generic Transferable for getting the data nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, NS_GET_IID(nsITransferable), (void**)transferable); if (NS_FAILED(rv)) return rv; // Get the nsITransferable interface for getting the data from the clipboard if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime); return NS_OK; } NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *transferable) { nsresult rv = NS_OK; char* bestFlavor = nsnull; nsCOMPtr genericDataObj; PRUint32 len = 0; if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) ) { nsAutoTxnsConserveSelection dontSpazMySelection(this); nsAutoString flavor, stuffToPaste; flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() if (flavor.Equals(NS_LITERAL_STRING(kUnicodeMime))) { nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); if (textDataObj && len > 0) { nsAutoString text; textDataObj->GetData ( text ); NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); stuffToPaste.Assign ( text.get(), len / 2 ); nsAutoEditBatch beginBatching(this); rv = InsertText(stuffToPaste); } } } nsCRT::free(bestFlavor); // Try to scroll the selection into view if the paste/drop succeeded if (NS_SUCCEEDED(rv)) ScrollSelectionIntoView(PR_FALSE); return rv; } NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) { ForceCompositionEnd(); nsresult rv; nsCOMPtr dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr dragSession; dragService->GetCurrentSession(getter_AddRefs(dragSession)); if (!dragSession) return NS_OK; // transferable hooks PRBool isAllowed = PR_TRUE; DoAllowDropHook(aDropEvent, dragSession, &isAllowed); if (!isAllowed) return NS_OK; // Get the nsITransferable interface for getting the data from the drop nsCOMPtr trans; rv = PrepareTransferable(getter_AddRefs(trans)); if (NS_FAILED(rv)) return rv; if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL? PRBool doInsert = PR_TRUE; DoInsertionHook(aDropEvent, trans, &doInsert); if (!doInsert) return NS_OK; PRUint32 numItems = 0; rv = dragSession->GetNumDropItems(&numItems); if (NS_FAILED(rv)) return rv; // Combine any deletion and drop insertion into one transaction nsAutoEditBatch beginBatching(this); PRUint32 i; PRBool doPlaceCaret = PR_TRUE; for (i = 0; i < numItems; ++i) { rv = dragSession->GetData(trans, i); if (NS_FAILED(rv)) return rv; if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail? if ( doPlaceCaret ) { // check if the user pressed the key to force a copy rather than a move // if we run into problems here, we'll just assume the user doesn't want a copy PRBool userWantsCopy = PR_FALSE; nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent)); if (!nsuiEvent) return NS_ERROR_FAILURE; nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) ); if (mouseEvent) #if defined(XP_MAC) || defined(XP_MACOSX) mouseEvent->GetAltKey(&userWantsCopy); #else mouseEvent->GetCtrlKey(&userWantsCopy); #endif // Source doc is null if source is *not* the current editor document nsCOMPtr srcdomdoc; rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc)); if (NS_FAILED(rv)) return rv; // Current doc is destination nsCOMPtrdestdomdoc; rv = GetDocument(getter_AddRefs(destdomdoc)); if (NS_FAILED(rv)) return rv; nsCOMPtr selection; rv = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(rv)) return rv; if (!selection) return NS_ERROR_FAILURE; PRBool isCollapsed; rv = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(rv)) return rv; // Parent and offset under the mouse cursor nsCOMPtr newSelectionParent; PRInt32 newSelectionOffset = 0; rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent)); if (NS_FAILED(rv)) return rv; if (!newSelectionParent) return NS_ERROR_FAILURE; rv = nsuiEvent->GetRangeOffset(&newSelectionOffset); if (NS_FAILED(rv)) return rv; // We never have to delete if selection is already collapsed PRBool deleteSelection = PR_FALSE; PRBool cursorIsInSelection = PR_FALSE; // Check if mouse is in the selection if (!isCollapsed) { PRInt32 rangeCount; rv = selection->GetRangeCount(&rangeCount); if (NS_FAILED(rv)) return rv; for (PRInt32 j = 0; j < rangeCount; j++) { nsCOMPtr range; rv = selection->GetRangeAt(j, getter_AddRefs(range)); if (NS_FAILED(rv) || !range) continue;//dont bail yet, iterate through them all nsCOMPtr nsrange(do_QueryInterface(range)); if (NS_FAILED(rv) || !nsrange) continue;//dont bail yet, iterate through them all rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection); if(cursorIsInSelection) break; } if (cursorIsInSelection) { // Dragging within same doc can't drop on itself -- leave! // (We shouldn't get here - drag event shouldn't have started if over selection) if (srcdomdoc == destdomdoc) return NS_OK; // Dragging from another window onto a selection // XXX Decision made to NOT do this, // note that 4.x does replace if dropped on //deleteSelection = PR_TRUE; } else { // We are NOT over the selection if (srcdomdoc == destdomdoc) { // Within the same doc: delete if user doesn't want to copy deleteSelection = !userWantsCopy; } else { // Different source doc: Don't delete deleteSelection = PR_FALSE; } } } if (deleteSelection) { // Use an auto tracker so that our drop point is correctly // positioned after the delete. nsAutoTrackDOMPoint tracker(mRangeUpdater, &newSelectionParent, &newSelectionOffset); rv = DeleteSelection(eNone); if (NS_FAILED(rv)) return rv; } // If we deleted the selection because we dropped from another doc, // then we don't have to relocate the caret (insert at the deletion point) if (!(deleteSelection && srcdomdoc != destdomdoc)) { // Move the selection to the point under the mouse cursor selection->Collapse(newSelectionParent, newSelectionOffset); } // We have to figure out whether to delete and relocate caret only once doPlaceCaret = PR_FALSE; } rv = InsertTextFromTransferable(trans); } return rv; } NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool *aCanDrag) { if (!aCanDrag) return NS_ERROR_NULL_POINTER; /* we really should be checking the XY coordinates of the mouseevent and ensure that * that particular point is actually within the selection (not just that there is a selection) */ *aCanDrag = PR_FALSE; // KLUDGE to work around bug 50703 // After double click and object property editing, // we get a spurious drag event if (mIgnoreSpuriousDragEvent) { mIgnoreSpuriousDragEvent = PR_FALSE; return NS_OK; } nsCOMPtr selection; nsresult res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; PRBool isCollapsed; res = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(res)) return res; // if we are collapsed, we have no selection so nothing to drag if ( isCollapsed ) return NS_OK; nsCOMPtr eventTarget; nsCOMPtr nsevent(do_QueryInterface(aDragEvent)); if (nsevent) { res = nsevent->GetExplicitOriginalTarget(getter_AddRefs(eventTarget)); if (NS_FAILED(res)) return res; } if (eventTarget) { nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget); if ( eventTargetDomNode ) { PRBool isTargetedCorrectly = PR_FALSE; res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &isTargetedCorrectly); if (NS_FAILED(res)) return res; *aCanDrag = isTargetedCorrectly; } } if (NS_FAILED(res)) return res; if (!*aCanDrag) return NS_OK; nsCOMPtr domdoc; GetDocument(getter_AddRefs(domdoc)); nsCOMPtr doc = do_QueryInterface(domdoc); if (!doc) return NS_ERROR_FAILURE; nsCOMPtr isupp; doc->GetContainer(getter_AddRefs(isupp)); nsCOMPtr docShell = do_QueryInterface(isupp); nsCOMPtr hookObj = do_GetInterface(docShell); if (!hookObj) return NS_ERROR_FAILURE; nsCOMPtr enumerator; hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); if (!enumerator) return NS_ERROR_FAILURE; nsCOMPtr override; PRBool hasMoreHooks = PR_FALSE; while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks) { res = enumerator->GetNext(getter_AddRefs(isupp)); if (NS_FAILED(res)) break; override = do_QueryInterface(isupp); if (override) { res = override->AllowStartDrag(aDragEvent, aCanDrag); NS_ASSERTION(NS_SUCCEEDED(res), "hook failure in AllowStartDrag"); } if (!*aCanDrag) break; } return NS_OK; } NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent) { nsresult rv; nsCOMPtr trans; rv = PutDragDataInTransferable(getter_AddRefs(trans)); if (NS_FAILED(rv)) return rv; if (!trans) return NS_OK; // maybe there was nothing to copy? /* get the drag service */ nsCOMPtr dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv); if (NS_FAILED(rv)) return rv; /* create an array of transferables */ nsCOMPtr transferableArray; NS_NewISupportsArray(getter_AddRefs(transferableArray)); if (transferableArray == nsnull) return NS_ERROR_OUT_OF_MEMORY; /* add the transferable to the array */ rv = transferableArray->AppendElement(trans); if (NS_FAILED(rv)) return rv; // check our transferable hooks (if any) nsCOMPtr domdoc; GetDocument(getter_AddRefs(domdoc)); nsCOMPtr doc = do_QueryInterface(domdoc); if (!doc) return NS_ERROR_FAILURE; nsCOMPtr isupp; doc->GetContainer(getter_AddRefs(isupp)); nsCOMPtr docShell = do_QueryInterface(isupp); nsCOMPtr hookObj = do_GetInterface(docShell); if (!hookObj) return NS_ERROR_FAILURE; nsCOMPtr enumerator; hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); if (!enumerator) return NS_ERROR_FAILURE; nsCOMPtr override; PRBool canInvokeDrag = PR_TRUE; PRBool hasMoreHooks = PR_FALSE; while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks) { rv = enumerator->GetNext(getter_AddRefs(isupp)); if (NS_FAILED(rv)) break; override = do_QueryInterface(isupp); if (override) { nsresult hookResult = override->OnCopyOrDrag(trans, &canInvokeDrag); NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in OnCopyOrDrag"); } // if one of our overrides says we can't invoke drag, return now if (!canInvokeDrag) return NS_OK; } /* invoke drag */ nsCOMPtr eventTarget; rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget)); if (NS_FAILED(rv)) return rv; nsCOMPtr domnode = do_QueryInterface(eventTarget); unsigned int flags; // in some cases we'll want to cut rather than copy... hmmmmm... flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; rv = dragService->InvokeDragSession(domnode, transferableArray, nsnull, flags); if (NS_FAILED(rv)) return rv; nsCOMPtr nsevent(do_QueryInterface(aDragEvent)); if (nsevent) nsevent->PreventBubble(); return rv; } NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType) { ForceCompositionEnd(); // Get Clipboard Service nsresult rv; nsCOMPtr clipboard( do_GetService( kCClipboardCID, &rv ) ); if ( NS_FAILED(rv) ) return rv; // Get the nsITransferable interface for getting the data from the clipboard nsCOMPtr trans; rv = PrepareTransferable(getter_AddRefs(trans)); if (NS_SUCCEEDED(rv) && trans) { // handle transferable hooks PRBool doInsert = PR_TRUE; DoInsertionHook(nsnull, trans, &doInsert); if (!doInsert) return NS_OK; // Get the Data from the clipboard if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) { rv = InsertTextFromTransferable(trans); } } return rv; } NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste) { if (!aCanPaste) return NS_ERROR_NULL_POINTER; *aCanPaste = PR_FALSE; // can't paste if readonly if (!IsModifiable()) return NS_OK; nsresult rv; nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); if (NS_FAILED(rv)) return rv; // the flavors that we can deal with const char* const textEditorFlavors[] = { kUnicodeMime, nsnull }; nsCOMPtr flavorsList; rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList)); if (NS_FAILED(rv)) return rv; PRUint32 editorFlags; GetFlags(&editorFlags); // add the flavors for text editors for (const char* const* flavor = textEditorFlavors; *flavor; flavor++) { nsCOMPtr flavorString; nsComponentManager::CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull, NS_GET_IID(nsISupportsCString), getter_AddRefs(flavorString)); if (flavorString) { flavorString->SetData(nsDependentCString(*flavor)); flavorsList->AppendElement(flavorString); } } PRBool haveFlavors; rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors); if (NS_FAILED(rv)) return rv; *aCanPaste = haveFlavors; return NS_OK; } nsresult nsPlaintextEditor::SetupDocEncoder(nsIDocumentEncoder **aDocEncoder) { nsCOMPtr domdoc; nsresult rv = GetDocument(getter_AddRefs(domdoc)); if (NS_FAILED(rv)) return rv; nsCOMPtr doc = do_QueryInterface(domdoc); if (!doc) return NS_ERROR_FAILURE; // find out if we're a plaintext control or not PRUint32 editorFlags = 0; rv = GetFlags(&editorFlags); if (NS_FAILED(rv)) return rv; PRBool bIsPlainTextControl = ((editorFlags & eEditorPlaintextMask) != 0); // get correct mimeType and document encoder flags set nsAutoString mimeType; PRUint32 docEncoderFlags = 0; if (bIsPlainTextControl) { docEncoderFlags |= nsIDocumentEncoder::OutputBodyOnly | nsIDocumentEncoder::OutputPreformatted; mimeType = NS_LITERAL_STRING(kUnicodeMime); } else mimeType = NS_LITERAL_STRING(kHTMLMime); // set up docEncoder nsCOMPtr encoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID); if (!encoder) return NS_ERROR_OUT_OF_MEMORY; rv = encoder->Init(doc, mimeType, docEncoderFlags); if (NS_FAILED(rv)) return rv; /* get the selection to be dragged */ nsCOMPtr selection; rv = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(rv)) return rv; rv = encoder->SetSelection(selection); if (NS_FAILED(rv)) return rv; *aDocEncoder = encoder; NS_ADDREF(*aDocEncoder); return NS_OK; } nsresult nsPlaintextEditor::PutDragDataInTransferable(nsITransferable **aTransferable) { *aTransferable = nsnull; nsCOMPtr docEncoder; nsresult rv = SetupDocEncoder(getter_AddRefs(docEncoder)); if (NS_FAILED(rv)) return rv; // grab a string nsAutoString buffer; rv = docEncoder->EncodeToString(buffer); if (NS_FAILED(rv)) return rv; // if we have an empty string, we're done; otherwise continue if (buffer.IsEmpty()) return NS_OK; nsCOMPtr dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = dataWrapper->SetData(buffer); if (NS_FAILED(rv)) return rv; /* create html flavor transferable */ nsCOMPtr trans = do_CreateInstance(kCTransferableCID, &rv); NS_ENSURE_SUCCESS(rv, rv); // find out if we're a plaintext control or not PRUint32 editorFlags = 0; rv = GetFlags(&editorFlags); if (NS_FAILED(rv)) return rv; PRBool bIsPlainTextControl = ((editorFlags & eEditorPlaintextMask) != 0); if (bIsPlainTextControl) { // Add the unicode flavor to the transferable rv = trans->AddDataFlavor(kUnicodeMime); if (NS_FAILED(rv)) return rv; } else { rv = trans->AddDataFlavor(kHTMLMime); if (NS_FAILED(rv)) return rv; nsCOMPtr htmlConverter = do_CreateInstance(kCHTMLFormatConverterCID); NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE); rv = trans->SetConverter(htmlConverter); if (NS_FAILED(rv)) return rv; } // QI the data object an |nsISupports| so that when the transferable holds // onto it, it will addref the correct interface. nsCOMPtr nsisupportsDataWrapper = do_QueryInterface(dataWrapper); rv = trans->SetTransferData(bIsPlainTextControl ? kUnicodeMime : kHTMLMime, nsisupportsDataWrapper, buffer.Length() * sizeof(PRUnichar)); if (NS_FAILED(rv)) return rv; *aTransferable = trans; NS_ADDREF(*aTransferable); return NS_OK; } nsresult nsPlaintextEditor::DoAllowDropHook(nsIDOMEvent* aDropEvent, nsIDragSession *aSession, PRBool *aAllowDrop) { *aAllowDrop = PR_TRUE; nsCOMPtr domdoc; GetDocument(getter_AddRefs(domdoc)); nsCOMPtr doc = do_QueryInterface(domdoc); if (!doc) return NS_ERROR_FAILURE; nsCOMPtr isupp; doc->GetContainer(getter_AddRefs(isupp)); nsCOMPtr docShell = do_QueryInterface(isupp); nsCOMPtr hookObj = do_GetInterface(docShell); if (!hookObj) return NS_ERROR_FAILURE; nsCOMPtr enumerator; hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); if (!enumerator) return NS_ERROR_FAILURE; nsCOMPtr override; PRBool hasMoreHooks = PR_FALSE; nsresult res = NS_OK; while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks) { res = enumerator->GetNext(getter_AddRefs(isupp)); if (NS_FAILED(res)) break; override = do_QueryInterface(isupp); if (override) { nsresult hookResult = override->AllowDrop(aDropEvent, aSession, aAllowDrop); NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in AllowDrop"); } if (!*aAllowDrop) break; } return res; } nsresult nsPlaintextEditor::DoInsertionHook(nsIDOMEvent* aDropEvent, nsITransferable *aTrans, PRBool *aDoInsert) { nsCOMPtr domdoc; GetDocument(getter_AddRefs(domdoc)); nsCOMPtr doc = do_QueryInterface(domdoc); if (!doc) return NS_ERROR_FAILURE; nsCOMPtr isupp; doc->GetContainer(getter_AddRefs(isupp)); nsCOMPtr docShell = do_QueryInterface(isupp); nsCOMPtr hookObj = do_GetInterface(docShell); if (!hookObj) return NS_ERROR_FAILURE; nsCOMPtr enumerator; hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); if (!enumerator) return NS_ERROR_FAILURE; nsCOMPtr override; PRBool hasMoreHooks = PR_FALSE; nsresult res = NS_OK; while (*aDoInsert && NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks) { res = enumerator->GetNext(getter_AddRefs(isupp)); if (NS_FAILED(res)) break; override = do_QueryInterface(isupp); if (override) { nsresult hookResult = override->OnPasteOrDrop(aDropEvent, aTrans, aDoInsert); NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in OnPasteOrDrop"); } } return res; }