Adding support for cut, copy, paste events. Patch by mfenniak-moz@mathieu.fenniak.net, r=Olli.Pettay@gmail.com, sr=jst@mozilla.org,jonas@sicking.cc
authorjst@mozilla.org
Wed, 25 Jul 2007 21:14:33 -0700
changeset 4010 fbdab51d84331cdce90167d90a251c7169b68b44
parent 4009 965af643450927b506474c137c1db85c7f8c688c
child 4011 321355ffdf116124c854a686673fece5c0640cbb
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersOlli, jst
milestone1.9a7pre
Adding support for cut, copy, paste events. Patch by mfenniak-moz@mathieu.fenniak.net, r=Olli.Pettay@gmail.com, sr=jst@mozilla.org,jonas@sicking.cc
content/base/public/nsCopySupport.h
content/base/src/nsContentUtils.cpp
content/base/src/nsCopySupport.cpp
content/base/src/nsGkAtomList.h
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMEvent.h
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsDOMClassInfo.h
dom/src/base/nsDOMWindowUtils.cpp
editor/libeditor/html/nsHTMLDataTransfer.cpp
editor/libeditor/text/nsPlaintextDataTransfer.cpp
editor/libeditor/text/nsPlaintextEditor.cpp
editor/libeditor/text/nsPlaintextEditor.h
layout/base/nsDocumentViewer.cpp
widget/public/nsGUIEvent.h
--- a/content/base/public/nsCopySupport.h
+++ b/content/base/public/nsCopySupport.h
@@ -42,16 +42,17 @@
 
 class nsISelection;
 class nsIDocument;
 class nsIImageLoadingContent;
 class nsIContent;
 class nsITransferable;
 class nsACString;
 class nsAString;
+class nsIDOMNode;
 
 class nsCopySupport
 {
   // class of static helper functions for copy support
   public:
     static nsresult HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID);
     static nsresult DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
                             PRBool *aDoPutOnClipboard);
@@ -59,11 +60,16 @@ class nsCopySupport
 
     // Get the selection, or entire document, in the format specified by the mime type
     // (text/html or text/plain). If aSel is non-null, use it, otherwise get the entire
     // doc.
     static nsresult GetContents(const nsACString& aMimeType, PRUint32 aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata);
     
     static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
                               PRInt32 aCopyFlags);
+
+    // Given the current selection, find the target that
+    // before[copy,cut,paste] and [copy,cut,paste] events will fire on.
+    static nsresult GetClipboardEventTarget(nsISelection *aSel,
+                                            nsIDOMNode **aEventTarget);
 };
 
 #endif
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -370,16 +370,22 @@ nsContentUtils::InitializeEventTable() {
     { &nsGkAtoms::onDOMMouseScroll,              { NS_MOUSE_SCROLL, EventNameType_HTMLXUL }},
     { &nsGkAtoms::oninput,                       { NS_FORM_INPUT, EventNameType_HTMLXUL }},
     { &nsGkAtoms::onpageshow,                    { NS_PAGE_SHOW, EventNameType_HTML }},
     { &nsGkAtoms::onpagehide,                    { NS_PAGE_HIDE, EventNameType_HTML }},
     { &nsGkAtoms::onresize,                      { NS_RESIZE_EVENT,
                                                  (EventNameType_HTMLXUL | EventNameType_SVGSVG) }},
     { &nsGkAtoms::onscroll,                      { NS_SCROLL_EVENT,
                                                  (EventNameType_HTMLXUL | EventNameType_SVGSVG) }},
+    { &nsGkAtoms::oncopy,                        { NS_COPY, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::oncut,                         { NS_CUT, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::onpaste,                       { NS_PASTE, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::onbeforecopy,                  { NS_BEFORECOPY, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::onbeforecut,                   { NS_BEFORECUT, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::onbeforepaste,                 { NS_BEFOREPASTE, EventNameType_HTMLXUL }},
     // XUL specific events
     { &nsGkAtoms::ontext,                        { NS_TEXT_TEXT, EventNameType_XUL }},
     { &nsGkAtoms::oncompositionstart,            { NS_COMPOSITION_START, EventNameType_XUL }},
     { &nsGkAtoms::oncompositionend,              { NS_COMPOSITION_END, EventNameType_XUL }},
     { &nsGkAtoms::onclose,                       { NS_XUL_CLOSE, EventNameType_XUL }},
     { &nsGkAtoms::onpopupshowing,                { NS_XUL_POPUP_SHOWING, EventNameType_XUL }},
     { &nsGkAtoms::onpopupshown,                  { NS_XUL_POPUP_SHOWN, EventNameType_XUL }},
     { &nsGkAtoms::onpopuphiding,                 { NS_XUL_POPUP_HIDING, EventNameType_XUL }},
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -540,8 +540,32 @@ static nsresult AppendDOMNode(nsITransfe
   if (!info.IsEmpty()) {
     rv = AppendString(aTransferable, info, kHTMLInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // add a special flavor, even if we don't have html context data
   return AppendString(aTransferable, context, kHTMLContext);
 }
+
+// Find the target that onbefore[copy,cut,paste] and on[copy,cut,paste]
+// events will fire on -- the start node of the copy selection.
+nsresult nsCopySupport::GetClipboardEventTarget(nsISelection *aSel,
+                                                nsIDOMNode **aEventTarget)
+{
+  NS_ENSURE_ARG(aSel);
+  NS_ENSURE_ARG_POINTER(aEventTarget);
+  *aEventTarget = nsnull;
+
+  nsCOMPtr<nsIDOMRange> range;
+  nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
+  if (rv == NS_ERROR_INVALID_ARG) // empty selection
+    return NS_ERROR_FAILURE;
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!range)
+    return NS_ERROR_FAILURE;
+
+  rv = range->GetStartContainer(aEventTarget);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return (*aEventTarget) ? NS_OK : NS_ERROR_FAILURE;
+}
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -540,27 +540,32 @@ GK_ATOM(object, "object")
 GK_ATOM(objectType, "object-type")
 GK_ATOM(observer, "observer")
 GK_ATOM(observes, "observes")
 GK_ATOM(odd, "odd")
 GK_ATOM(OFF, "OFF")
 GK_ATOM(ol, "ol")
 GK_ATOM(omitXmlDeclaration, "omit-xml-declaration")
 GK_ATOM(onabort, "onabort")
+GK_ATOM(onbeforecopy, "onbeforecopy")
+GK_ATOM(onbeforecut, "onbeforecut")
+GK_ATOM(onbeforepaste, "onbeforepaste")
 GK_ATOM(onbeforeunload, "onbeforeunload")
 GK_ATOM(onblur, "onblur")
 GK_ATOM(onbroadcast, "onbroadcast")
 GK_ATOM(onchange, "onchange")
 GK_ATOM(onclick, "onclick")
 GK_ATOM(onclose, "onclose")
 GK_ATOM(oncommand, "oncommand")
 GK_ATOM(oncommandupdate, "oncommandupdate")
 GK_ATOM(oncompositionend, "oncompositionend")
 GK_ATOM(oncompositionstart, "oncompositionstart")
 GK_ATOM(oncontextmenu, "oncontextmenu")
+GK_ATOM(oncopy, "oncopy")
+GK_ATOM(oncut, "oncut")
 GK_ATOM(ondblclick, "ondblclick")
 GK_ATOM(onDOMActivate, "onDOMActivate")
 GK_ATOM(onDOMAttrModified, "onDOMAttrModified")
 GK_ATOM(onDOMCharacterDataModified, "onDOMCharacterDataModified")
 GK_ATOM(onDOMFocusIn, "onDOMFocusIn")
 GK_ATOM(onDOMFocusOut, "onDOMFocusOut")
 GK_ATOM(onDOMMouseScroll, "onDOMMouseScroll")
 GK_ATOM(onDOMNodeInserted, "onDOMNodeInserted")
@@ -594,16 +599,17 @@ GK_ATOM(onmouseover, "onmouseover")
 GK_ATOM(onmouseup, "onmouseup")
 GK_ATOM(ononline, "ononline")
 GK_ATOM(onoffline, "onoffline")
 GK_ATOM(onoverflow, "onoverflow")
 GK_ATOM(onoverflowchanged, "onoverflowchanged")
 GK_ATOM(onpagehide, "onpagehide")
 GK_ATOM(onpageshow, "onpageshow")
 GK_ATOM(onpaint, "onpaint")
+GK_ATOM(onpaste, "onpaste")
 GK_ATOM(onpopuphidden, "onpopuphidden")
 GK_ATOM(onpopuphiding, "onpopuphiding")
 GK_ATOM(onpopupshowing, "onpopupshowing")
 GK_ATOM(onpopupshown, "onpopupshown")
 GK_ATOM(onRequest, "onRequest")
 GK_ATOM(onreset, "onreset")
 GK_ATOM(onresize, "onresize")
 GK_ATOM(onscroll, "onscroll")
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -65,17 +65,18 @@ static const char* const sEventNames[] =
   "popuphiding", "popuphidden", "close", "command", "broadcast", "commandupdate",
   "dragenter", "dragover", "dragexit", "dragdrop", "draggesture",
   "drag", "dragend", "dragstart", "dragleave", "drop", "resize",
   "scroll", "overflow", "underflow", "overflowchanged",
   "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", 
   "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument",
   "DOMAttrModified", "DOMCharacterDataModified",
   "DOMActivate", "DOMFocusIn", "DOMFocusOut",
-  "pageshow", "pagehide", "DOMMouseScroll", "offline", "online"
+  "pageshow", "pagehide", "DOMMouseScroll", "offline", "online",
+  "copy", "cut", "paste", "beforecopy", "beforecut", "beforepaste"
 #ifdef MOZ_SVG
  ,
   "SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll",
   "SVGZoom"
 #endif // MOZ_SVG
 };
 
 static char *sPopupAllowedEvents;
@@ -492,16 +493,28 @@ nsDOMEvent::SetEventType(const nsAString
     else if (atom == nsGkAtoms::onabort)
       mEvent->message = NS_IMAGE_ABORT;
     else if (atom == nsGkAtoms::onerror)
       mEvent->message = NS_LOAD_ERROR;
     else if (atom == nsGkAtoms::onoffline)
       mEvent->message = NS_OFFLINE;
     else if (atom == nsGkAtoms::ononline)
       mEvent->message = NS_ONLINE;
+    else if (atom == nsGkAtoms::oncopy)
+      mEvent->message = NS_COPY;
+    else if (atom == nsGkAtoms::oncut)
+      mEvent->message = NS_CUT;
+    else if (atom == nsGkAtoms::onpaste)
+      mEvent->message = NS_PASTE;
+    else if (atom == nsGkAtoms::onbeforecopy)
+      mEvent->message = NS_BEFORECOPY;
+    else if (atom == nsGkAtoms::onbeforecut)
+      mEvent->message = NS_BEFORECUT;
+    else if (atom == nsGkAtoms::onbeforepaste)
+      mEvent->message = NS_BEFOREPASTE;
   } else if (mEvent->eventStructType == NS_MUTATION_EVENT) {
     if (atom == nsGkAtoms::onDOMAttrModified)
       mEvent->message = NS_MUTATION_ATTRMODIFIED;
     else if (atom == nsGkAtoms::onDOMCharacterDataModified)
       mEvent->message = NS_MUTATION_CHARACTERDATAMODIFIED;
     else if (atom == nsGkAtoms::onDOMNodeInserted)
       mEvent->message = NS_MUTATION_NODEINSERTED;
     else if (atom == nsGkAtoms::onDOMNodeRemoved)
@@ -1296,16 +1309,28 @@ const char* nsDOMEvent::GetEventName(PRU
   case NS_PAGE_HIDE:
     return sEventNames[eDOMEvents_pagehide];
   case NS_MOUSE_SCROLL:
     return sEventNames[eDOMEvents_DOMMouseScroll];
   case NS_OFFLINE:
     return sEventNames[eDOMEvents_offline];
   case NS_ONLINE:
     return sEventNames[eDOMEvents_online];
+  case NS_COPY:
+    return sEventNames[eDOMEvents_copy];
+  case NS_CUT:
+    return sEventNames[eDOMEvents_cut];
+  case NS_PASTE:
+    return sEventNames[eDOMEvents_paste];
+  case NS_BEFORECOPY:
+    return sEventNames[eDOMEvents_beforecopy];
+  case NS_BEFORECUT:
+    return sEventNames[eDOMEvents_beforecut];
+  case NS_BEFOREPASTE:
+    return sEventNames[eDOMEvents_beforepaste];
 #ifdef MOZ_SVG
   case NS_SVG_LOAD:
     return sEventNames[eDOMEvents_SVGLoad];
   case NS_SVG_UNLOAD:
     return sEventNames[eDOMEvents_SVGUnload];
   case NS_SVG_ABORT:
     return sEventNames[eDOMEvents_SVGAbort];
   case NS_SVG_ERROR:
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -120,17 +120,23 @@ public:
     eDOMEvents_characterdatamodified,
     eDOMEvents_DOMActivate,
     eDOMEvents_DOMFocusIn,
     eDOMEvents_DOMFocusOut,
     eDOMEvents_pageshow,
     eDOMEvents_pagehide,
     eDOMEvents_DOMMouseScroll,
     eDOMEvents_offline,
-    eDOMEvents_online
+    eDOMEvents_online,
+    eDOMEvents_copy,
+    eDOMEvents_cut,
+    eDOMEvents_paste,
+    eDOMEvents_beforecopy,
+    eDOMEvents_beforecut,
+    eDOMEvents_beforepaste
 #ifdef MOZ_SVG
    ,
     eDOMEvents_SVGLoad,
     eDOMEvents_SVGUnload,
     eDOMEvents_SVGAbort,
     eDOMEvents_SVGError,
     eDOMEvents_SVGResize,
     eDOMEvents_SVGScroll,
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -1300,16 +1300,22 @@ jsval nsDOMClassInfo::sSelf_id          
 jsval nsDOMClassInfo::sOpener_id          = JSVAL_VOID;
 jsval nsDOMClassInfo::sAdd_id             = JSVAL_VOID;
 jsval nsDOMClassInfo::sAll_id             = JSVAL_VOID;
 jsval nsDOMClassInfo::sTags_id            = JSVAL_VOID;
 jsval nsDOMClassInfo::sAddEventListener_id= JSVAL_VOID;
 jsval nsDOMClassInfo::sBaseURIObject_id   = JSVAL_VOID;
 jsval nsDOMClassInfo::sNodePrincipal_id   = JSVAL_VOID;
 jsval nsDOMClassInfo::sDocumentURIObject_id=JSVAL_VOID;
+jsval nsDOMClassInfo::sOncopy_id          = JSVAL_VOID;
+jsval nsDOMClassInfo::sOncut_id           = JSVAL_VOID;
+jsval nsDOMClassInfo::sOnpaste_id         = JSVAL_VOID;
+jsval nsDOMClassInfo::sOnbeforecopy_id    = JSVAL_VOID;
+jsval nsDOMClassInfo::sOnbeforecut_id     = JSVAL_VOID;
+jsval nsDOMClassInfo::sOnbeforepaste_id   = JSVAL_VOID;
 
 const JSClass *nsDOMClassInfo::sObjectClass = nsnull;
 const JSClass *nsDOMClassInfo::sXPCNativeWrapperClass = nsnull;
 
 PRBool nsDOMClassInfo::sDoSecurityCheckInAddProperty = PR_TRUE;
 
 const JSClass*
 NS_DOMClassInfo_GetXPCNativeWrapperClass()
@@ -1491,16 +1497,22 @@ nsDOMClassInfo::DefineStaticJSVals(JSCon
   SET_JSVAL_TO_STRING(sOpener_id,          cx, "opener");
   SET_JSVAL_TO_STRING(sAdd_id,             cx, "add");
   SET_JSVAL_TO_STRING(sAll_id,             cx, "all");
   SET_JSVAL_TO_STRING(sTags_id,            cx, "tags");
   SET_JSVAL_TO_STRING(sAddEventListener_id,cx, "addEventListener");
   SET_JSVAL_TO_STRING(sBaseURIObject_id,   cx, "baseURIObject");
   SET_JSVAL_TO_STRING(sNodePrincipal_id,   cx, "nodePrincipal");
   SET_JSVAL_TO_STRING(sDocumentURIObject_id,cx,"documentURIObject");
+  SET_JSVAL_TO_STRING(sOncopy_id,          cx, "oncopy");
+  SET_JSVAL_TO_STRING(sOncut_id,           cx, "oncut");
+  SET_JSVAL_TO_STRING(sOnpaste_id,         cx, "onpaste");
+  SET_JSVAL_TO_STRING(sOnbeforecopy_id,    cx, "oncopy");
+  SET_JSVAL_TO_STRING(sOnbeforecut_id,     cx, "oncut");
+  SET_JSVAL_TO_STRING(sOnbeforepaste_id,   cx, "onpaste");
 
   return NS_OK;
 }
 
 // static
 nsresult
 nsDOMClassInfo::WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
                            const nsIID& aIID, jsval *vp,
@@ -3967,16 +3979,22 @@ nsDOMClassInfo::ShutDown()
   sOpener_id          = JSVAL_VOID;
   sAdd_id             = JSVAL_VOID;
   sAll_id             = JSVAL_VOID;
   sTags_id            = JSVAL_VOID;
   sAddEventListener_id= JSVAL_VOID;
   sBaseURIObject_id   = JSVAL_VOID;
   sNodePrincipal_id   = JSVAL_VOID;
   sDocumentURIObject_id=JSVAL_VOID;
+  sOncopy_id          = JSVAL_VOID;
+  sOncut_id           = JSVAL_VOID;
+  sOnpaste_id         = JSVAL_VOID;
+  sOnbeforecopy_id    = JSVAL_VOID;
+  sOnbeforecut_id     = JSVAL_VOID;
+  sOnbeforepaste_id   = JSVAL_VOID;
 
   NS_IF_RELEASE(sXPConnect);
   NS_IF_RELEASE(sSecMan);
   sIsInitialized = PR_FALSE;
 }
 
 
 static const nsIXPConnectWrappedNative *cached_win_wrapper;
@@ -6624,33 +6642,39 @@ nsEventReceiverSH::ReallyIsEventName(jsv
 {
   // I wonder if this is faster than using a hash...
 
   switch (aFirstChar) {
   case 'a' :
     return id == sOnabort_id;
   case 'b' :
     return (id == sOnbeforeunload_id ||
-            id == sOnblur_id);
+            id == sOnblur_id         ||
+            id == sOnbeforecopy_id   ||
+            id == sOnbeforecut_id    ||
+            id == sOnbeforepaste_id);
   case 'e' :
     return id == sOnerror_id;
   case 'f' :
     return id == sOnfocus_id;
   case 'c' :
     return (id == sOnchange_id       ||
             id == sOnclick_id        ||
-            id == sOncontextmenu_id);
+            id == sOncontextmenu_id  ||
+            id == sOncopy_id         ||
+            id == sOncut_id);
   case 'd' :
     return id == sOndblclick_id;
   case 'l' :
     return id == sOnload_id;
   case 'p' :
     return (id == sOnpaint_id        ||
             id == sOnpageshow_id     ||
-            id == sOnpagehide_id);
+            id == sOnpagehide_id     ||
+            id == sOnpaste_id);
   case 'k' :
     return (id == sOnkeydown_id      ||
             id == sOnkeypress_id     ||
             id == sOnkeyup_id);
   case 'u' :
     return id == sOnunload_id;
   case 'm' :
     return (id == sOnmousemove_id    ||
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -311,16 +311,22 @@ protected:
   static jsval sOpener_id;
   static jsval sAdd_id;
   static jsval sAll_id;
   static jsval sTags_id;
   static jsval sAddEventListener_id;
   static jsval sBaseURIObject_id;
   static jsval sNodePrincipal_id;
   static jsval sDocumentURIObject_id;
+  static jsval sOncopy_id;
+  static jsval sOncut_id;
+  static jsval sOnpaste_id;
+  static jsval sOnbeforecopy_id;
+  static jsval sOnbeforecut_id;
+  static jsval sOnbeforepaste_id;
 
   static const JSClass *sObjectClass;
   static const JSClass *sXPCNativeWrapperClass;
 
 public:
   static PRBool sDoSecurityCheckInAddProperty;
 };
 
--- a/dom/src/base/nsDOMWindowUtils.cpp
+++ b/dom/src/base/nsDOMWindowUtils.cpp
@@ -219,17 +219,17 @@ nsDOMWindowUtils::SendKeyEvent(const nsA
                                PRInt32 aModifiers)
 {
   PRBool hasCap = PR_FALSE;
   if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
       || !hasCap)
     return NS_ERROR_DOM_SECURITY_ERR;
 
   // get the widget to send the event to
-  nsIWidget* widget = GetWidget();
+  nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
   PRInt32 msg;
   if (aType.EqualsLiteral("keydown"))
     msg = NS_KEY_DOWN;
   else if (aType.EqualsLiteral("keyup"))
     msg = NS_KEY_UP;
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -129,16 +129,17 @@
 #include "nsIDOMHTMLBodyElement.h"
 
 // Misc
 #include "TextEditorTest.h"
 #include "nsEditorUtils.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIContentFilter.h"
+#include "nsEventDispatcher.h"
 
 const PRUnichar nbsp = 160;
 
 static NS_DEFINE_CID(kCParserCID,     NS_PARSER_CID);
 
 // private clipboard data flavors for html copy/paste
 #define kHTMLContext   "text/_moz_htmlcontext"
 #define kHTMLInfo      "text/_moz_htmlinfo"
@@ -1820,18 +1821,41 @@ PRBool nsHTMLEditor::HavePrivateHTMLFlav
   return PR_FALSE;
 }
 
 
 NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType)
 {
   ForceCompositionEnd();
 
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the paste event.
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_PASTE);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault)
+      return NS_OK;
+
+    // Did the event handler cause the editor to be destroyed?
+    // (ie. the input element was removed from the document)
+    // Don't proceed with paste.
+    if (mDidPreDestroy)
+      return NS_OK;
+  }
+
   // Get Clipboard Service
-  nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   if (NS_FAILED(rv))
     return rv;
   
   // find out if we have our internal html flavor on the clipboard.  We don't want to mess
   // around with cfhtml if we do.
   PRBool bHavePrivateHTMLFlavor = HavePrivateHTMLFlavor(clipboard);
 
@@ -1929,25 +1953,44 @@ NS_IMETHODIMP nsHTMLEditor::PasteNoForma
   }
 
   return rv;
 }
 
 
 NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
 {
-  if (!aCanPaste)
-    return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_ARG_POINTER(aCanPaste);
   *aCanPaste = PR_FALSE;
+
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the beforepaste event.  If the event handler requests to prevent
+  // default behavior, set *aCanPaste = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFOREPASTE);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aCanPaste = PR_TRUE;
+      return NS_OK;
+    }
+  }
   
   // can't paste if readonly
   if (!IsModifiable())
     return NS_OK;
     
-  nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   if (NS_FAILED(rv)) return rv;
   
   // the flavors that we can deal with
   const char* const textEditorFlavors[] = { kUnicodeMime, nsnull };
   const char* const htmlEditorFlavors[] = { kHTMLMime, kJPEGImageMime, nsnull };
 
   nsCOMPtr<nsISupportsArray> flavorsList =
--- a/editor/libeditor/text/nsPlaintextDataTransfer.cpp
+++ b/editor/libeditor/text/nsPlaintextDataTransfer.cpp
@@ -61,16 +61,17 @@
 #include "nsIDragService.h"
 #include "nsIDOMNSUIEvent.h"
 
 // Misc
 #include "nsEditorUtils.h"
 #include "nsContentCID.h"
 #include "nsISelectionPrivate.h"
 #include "nsFrameSelection.h"
+#include "nsEventDispatcher.h"
 
 NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
 {
   // Create generic Transferable for getting the data
   nsresult rv = CallCreateInstance("@mozilla.org/widget/transferable;1", transferable);
   if (NS_FAILED(rv))
     return rv;
 
@@ -421,18 +422,41 @@ NS_IMETHODIMP nsPlaintextEditor::DoDrag(
 
   return rv;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
 {
   ForceCompositionEnd();
 
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the paste event.
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_PASTE);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault)
+      return NS_OK;
+
+    // Did the event handler cause the editor to be destroyed?
+    // (ie. the input element was removed from the document)
+    // Don't proceed with paste, as it will crash.
+    if (mDidPreDestroy)
+      return NS_OK;
+  }
+
   // Get Clipboard Service
-  nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   if ( NS_FAILED(rv) )
     return rv;
 
   // Get the nsITransferable interface for getting the data from the clipboard
   nsCOMPtr<nsITransferable> trans;
   rv = PrepareTransferable(getter_AddRefs(trans));
   if (NS_SUCCEEDED(rv) && trans)
@@ -451,25 +475,44 @@ NS_IMETHODIMP nsPlaintextEditor::Paste(P
   }
 
   return rv;
 }
 
 
 NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
 {
-  if (!aCanPaste)
-    return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_ARG_POINTER(aCanPaste);
   *aCanPaste = PR_FALSE;
+
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the beforepaste event.  If the event handler requests to prevent
+  // default behavior, set *aCanPaste = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFOREPASTE);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aCanPaste = PR_TRUE;
+      return NS_OK;
+    }
+  }
   
   // can't paste if readonly
   if (!IsModifiable())
     return NS_OK;
 
-  nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   if (NS_FAILED(rv)) return rv;
   
   // the flavors that we can deal with
   const char* const textEditorFlavors[] = { kUnicodeMime, nsnull };
 
   nsCOMPtr<nsISupportsArray> flavorsList = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
 
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -73,20 +73,22 @@
 // Misc
 #include "nsEditorUtils.h"  // nsAutoEditBatch, nsAutoRules
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsUnicharUtils.h"
 #include "nsContentCID.h"
 #include "nsAOLCiter.h"
 #include "nsInternetCiter.h"
+#include "nsEventDispatcher.h"
 
 // Drag & Drop, Clipboard
 #include "nsIClipboard.h"
 #include "nsITransferable.h"
+#include "nsCopySupport.h"
 
 // prototype for rules creation shortcut
 nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult);
 
 nsPlaintextEditor::nsPlaintextEditor()
 : nsEditor()
 , mIgnoreSpuriousDragEvent(PR_FALSE)
 , mRules(nsnull)
@@ -1128,63 +1130,160 @@ nsPlaintextEditor::Redo(PRUint32 aCount)
   {
     result = nsEditor::Redo(aCount);
     result = mRules->DidDoAction(selection, &ruleInfo, result);
   } 
    
   return result;
 }
 
-NS_IMETHODIMP nsPlaintextEditor::Cut()
+nsresult nsPlaintextEditor::GetClipboardEventTarget(nsIDOMNode** aEventTarget)
 {
+  NS_ENSURE_ARG_POINTER(aEventTarget);
+  *aEventTarget = nsnull;
+
   nsCOMPtr<nsISelection> selection;
   nsresult res = GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res))
     return res;
 
+  return nsCopySupport::GetClipboardEventTarget(selection, aEventTarget);
+}
+
+NS_IMETHODIMP nsPlaintextEditor::Cut()
+{
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the cut event.
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_CUT);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault)
+      return NS_OK;
+  }
+
+  nsCOMPtr<nsISelection> selection;
+  rv = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(rv))
+    return rv;
+
   PRBool isCollapsed;
   if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed)
     return NS_OK;  // just return ok so no JS error is thrown
 
-  res = Copy();
-  if (NS_SUCCEEDED(res))
-    res = DeleteSelection(eNone);
-  return res;
+  rv = ps->DoCopy();
+  if (NS_SUCCEEDED(rv))
+    rv = DeleteSelection(eNone);
+  return rv;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::CanCut(PRBool *aCanCut)
 {
-  nsresult res = CanCopy(aCanCut);
-  if (NS_FAILED(res)) return res;
+  NS_ENSURE_ARG_POINTER(aCanCut);
+  *aCanCut = PR_FALSE;
+
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the beforecut event.  If the event handler requests to prevent
+  // default behavior, set *aCanCut = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFORECUT);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aCanCut = PR_TRUE;
+      return NS_OK;
+    }
+  }
+
+  nsCOMPtr<nsISelection> selection;
+  rv = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(rv)) return rv;
     
-  *aCanCut = *aCanCut && IsModifiable();
+  PRBool isCollapsed;
+  rv = selection->GetIsCollapsed(&isCollapsed);
+  if (NS_FAILED(rv)) return rv;
+
+  *aCanCut = !isCollapsed && IsModifiable();
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::Copy()
 {
-  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
-  if (!ps) return NS_ERROR_NOT_INITIALIZED;
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the copy event.
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_COPY);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault)
+      return NS_OK;
+    // the affect of the event handler closing the window here has been
+    // tested, it works without crashes.
+  }
+
   return ps->DoCopy();
 }
 
 NS_IMETHODIMP nsPlaintextEditor::CanCopy(PRBool *aCanCopy)
 {
-  if (!aCanCopy)
-    return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_ARG_POINTER(aCanCopy);
   *aCanCopy = PR_FALSE;
+
+  nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+  if (!ps)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // Fire the beforecopy event.  If the event handler requests to prevent
+  // default behavior, set *aCopyable = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFORECOPY);
+    nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
+                                nsnull, &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aCanCopy = PR_TRUE;
+      return NS_OK;
+    }
+  }
   
   nsCOMPtr<nsISelection> selection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
+  rv = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(rv)) return rv;
     
   PRBool isCollapsed;
-  res = selection->GetIsCollapsed(&isCollapsed);
-  if (NS_FAILED(res)) return res;
+  rv = selection->GetIsCollapsed(&isCollapsed);
+  if (NS_FAILED(rv)) return rv;
 
   *aCanCopy = !isCollapsed;
   return NS_OK;
 }
 
 
 // Shared between OutputToString and OutputToStream
 NS_IMETHODIMP
--- a/editor/libeditor/text/nsPlaintextEditor.h
+++ b/editor/libeditor/text/nsPlaintextEditor.h
@@ -209,16 +209,20 @@ protected:
 
   /* small utility routine to test the eEditorReadonly bit */
   PRBool IsModifiable();
 
   //XXX Kludge: Used to suppress spurious drag/drop events (bug 50703)
   PRBool   mIgnoreSpuriousDragEvent;
   NS_IMETHOD IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;}
 
+  // Wrapper for nsCopySupport::GetClipboardEventTarget, finds target to fire
+  // [cut,copy,paste] and [beforecut,beforecopy,beforepaste] events at.
+  virtual nsresult GetClipboardEventTarget(nsIDOMNode** aEventTarget);
+
 // Data members
 protected:
 
   nsCOMPtr<nsIEditRules>        mRules;
   PRBool  mWrapToWindow;
   PRInt32 mWrapColumn;
   PRInt32 mMaxTextLength;
   PRInt32 mInitTriggerCounter;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -368,16 +368,18 @@ private:
   void DumpContentToPPM(const char* aFileName);
 
   void PrepareToStartLoad(void);
 
   nsresult SyncParentSubDocMap();
 
   nsresult GetDocumentSelection(nsISelection **aSelection);
 
+  nsresult GetClipboardEventTarget(nsIDOMNode **aEventTarget);
+
 #ifdef NS_PRINTING
   // Called when the DocViewer is notified that the state
   // of Printing or PP has changed
   void SetIsPrintingInDocShellTree(nsIDocShellTreeNode* aParentNode, 
                                    PRBool               aIsPrintingOrPP, 
                                    PRBool               aStartAtTop);
 #endif // NS_PRINTING
 
@@ -2308,16 +2310,36 @@ NS_IMETHODIMP DocumentViewerImpl::Select
 
   rv = selection->SelectAllChildren(bodyNode);
   return rv;
 }
 
 NS_IMETHODIMP DocumentViewerImpl::CopySelection()
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
+
+  // Fire the copy event.
+  nsresult rv;
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_COPY);
+    nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt, nsnull,
+                                &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault)
+      // Skip default behavior, return OK.
+      return NS_OK;
+    // It's possible the oncopy handler closed the window.
+    if (!mPresShell)
+      return NS_OK;
+  }
+
   return mPresShell->DoCopy();
 }
 
 NS_IMETHODIMP DocumentViewerImpl::CopyLinkLocation()
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
   nsCOMPtr<nsIDOMNode> node;
   GetPopupLinkNode(getter_AddRefs(node));
@@ -2341,51 +2363,162 @@ NS_IMETHODIMP DocumentViewerImpl::CopyIm
   nsCOMPtr<nsIImageLoadingContent> node;
   GetPopupImageNode(getter_AddRefs(node));
   // make noise if we're not in an image
   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
 
   return nsCopySupport::ImageCopy(node, aCopyFlags);
 }
 
+nsresult DocumentViewerImpl::GetClipboardEventTarget(nsIDOMNode** aEventTarget)
+{
+  NS_ENSURE_ARG_POINTER(aEventTarget);
+  *aEventTarget = nsnull;
+
+  if (!mPresShell)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  nsCOMPtr<nsISelection> sel;
+  nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(sel));
+  if (NS_FAILED(rv))
+    return rv;
+  if (!sel)
+    return NS_ERROR_FAILURE;
+
+  return nsCopySupport::GetClipboardEventTarget(sel, aEventTarget);
+}
+
 NS_IMETHODIMP DocumentViewerImpl::GetCopyable(PRBool *aCopyable)
 {
+  NS_ENSURE_ARG_POINTER(aCopyable);
+  *aCopyable = PR_FALSE;
+
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 
+  // Fire the beforecopy event.  If the event handler requests to prevent
+  // default behavior, set *aCopyable = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFORECOPY);
+    nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt, nsnull,
+                                &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aCopyable = PR_TRUE;
+      return NS_OK;
+    }
+    // It's possible the onbeforecopy handler closed the window.
+    if (!mPresShell)
+      return NS_OK;
+  }
+
   nsCOMPtr<nsISelection> selection;
-  nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(selection));
+  rv = mPresShell->GetSelectionForCopy(getter_AddRefs(selection));
   if (NS_FAILED(rv)) return rv;
 
   PRBool isCollapsed;
   selection->GetIsCollapsed(&isCollapsed);
 
   *aCopyable = !isCollapsed;
   return NS_OK;
 }
 
 NS_IMETHODIMP DocumentViewerImpl::CutSelection()
 {
-  // Nothing to do here.
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_INITIALIZED);
+
+  // Fire the cut event.
+  nsresult rv;
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEvent evt(PR_TRUE, NS_CUT);
+    nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt);
+    // should skip default behavior here if event handler returns false, but
+    // there is no default behavior to worry about.
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP DocumentViewerImpl::GetCutable(PRBool *aCutable)
 {
+  NS_ENSURE_ARG_POINTER(aCutable);
+  *aCutable = PR_FALSE;
+
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_INITIALIZED);
+
+  // Fire the beforecut event.  If the event handler requests to prevent
+  // default behavior, set *aCutable = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFORECUT);
+    nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt, nsnull,
+                                &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aCutable = PR_TRUE;
+      return NS_OK;
+    }
+  }
+
   *aCutable = PR_FALSE;  // mm, will this ever be called for an editable document?
   return NS_OK;
 }
 
 NS_IMETHODIMP DocumentViewerImpl::Paste()
 {
-  // Nothing to do here.
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_INITIALIZED);
+
+  // Fire the paste event.
+  nsresult rv;
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEvent evt(PR_TRUE, NS_PASTE);
+    nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt);
+    // should skip default behavior here if event handler returns false, but
+    // there is no default behavior to worry about.
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP DocumentViewerImpl::GetPasteable(PRBool *aPasteable)
 {
+  NS_ENSURE_ARG_POINTER(aPasteable);
+  *aPasteable = PR_FALSE;
+
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_INITIALIZED);
+
+  // Fire the beforepaste event.  If the event handler requests to prevent
+  // default behavior, set *aPasteable = true.  (IE-style behavior)
+  nsCOMPtr<nsIDOMNode> eventTarget;
+  nsresult rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
+  // On failure to get event target, just forget about it and don't fire.
+  if (NS_SUCCEEDED(rv)) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsEvent evt(PR_TRUE, NS_BEFOREPASTE);
+    nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt, nsnull,
+                                &status);
+    // if event handler return'd false (PreventDefault)
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      *aPasteable = PR_TRUE;
+      return NS_OK;
+    }
+  }
+
   *aPasteable = PR_FALSE;
   return NS_OK;
 }
 
 /* AString getContents (in string mimeType, in boolean selectionOnly); */
 NS_IMETHODIMP DocumentViewerImpl::GetContents(const char *mimeType, PRBool selectionOnly, nsAString& aOutValue)
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -330,16 +330,25 @@ class nsHashKey;
 #define NS_SVGZOOM_EVENT_START          2900
 #define NS_SVG_ZOOM                     (NS_SVGZOOM_EVENT_START)
 #endif // MOZ_SVG
 
 // XUL command events
 #define NS_XULCOMMAND_EVENT_START       3000
 #define NS_XUL_COMMAND                  (NS_XULCOMMAND_EVENT_START)
 
+// Cut, copy, paste events
+#define NS_CUTCOPYPASTE_EVENT_START     3100
+#define NS_COPY             (NS_CUTCOPYPASTE_EVENT_START)
+#define NS_CUT              (NS_CUTCOPYPASTE_EVENT_START + 1)
+#define NS_PASTE            (NS_CUTCOPYPASTE_EVENT_START + 2)
+#define NS_BEFORECOPY       (NS_CUTCOPYPASTE_EVENT_START + 3)
+#define NS_BEFORECUT        (NS_CUTCOPYPASTE_EVENT_START + 4)
+#define NS_BEFOREPASTE      (NS_CUTCOPYPASTE_EVENT_START + 5)
+
 /**
  * Return status for event processors, nsEventStatus, is defined in
  * nsEvent.h.
  */
 
 /**
  * sizemode is an adjunct to widget size
  */