summaryrefslogtreecommitdiffstats
path: root/editor/libeditor
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor')
-rw-r--r--editor/libeditor/EditorBase.cpp23
-rw-r--r--editor/libeditor/EditorBase.h11
-rw-r--r--editor/libeditor/EditorEventListener.cpp315
-rw-r--r--editor/libeditor/EditorEventListener.h24
-rw-r--r--editor/libeditor/HTMLEditor.cpp12
-rw-r--r--editor/libeditor/HTMLEditor.h2
-rw-r--r--editor/libeditor/HTMLEditorEventListener.cpp24
-rw-r--r--editor/libeditor/TextEditor.cpp24
-rw-r--r--editor/libeditor/TextEditor.h3
9 files changed, 268 insertions, 170 deletions
diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp
index 0c4a2a41d..f7988cd1a 100644
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -5092,19 +5092,16 @@ EditorBase::IsActiveInDOMWindow()
}
bool
-EditorBase::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
+EditorBase::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
{
// If the event is trusted, the event should always cause input.
- NS_ENSURE_TRUE(aEvent, false);
-
- WidgetEvent* widgetEvent = aEvent->WidgetEventPtr();
- if (NS_WARN_IF(!widgetEvent)) {
+ if (NS_WARN_IF(!aGUIEvent)) {
return false;
}
// If this is dispatched by using cordinates but this editor doesn't have
// focus, we shouldn't handle it.
- if (widgetEvent->IsUsingCoordinates()) {
+ if (aGUIEvent->IsUsingCoordinates()) {
nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
if (!focusedContent) {
return false;
@@ -5117,8 +5114,7 @@ EditorBase::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
// Note that if we allow to handle such events, editor may be confused by
// strange event order.
bool needsWidget = false;
- WidgetGUIEvent* widgetGUIEvent = nullptr;
- switch (widgetEvent->mMessage) {
+ switch (aGUIEvent->mMessage) {
case eUnidentifiedEvent:
// If events are not created with proper event interface, their message
// are initialized with eUnidentifiedEvent. Let's ignore such event.
@@ -5130,25 +5126,26 @@ EditorBase::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
case eCompositionCommitAsIs:
// Don't allow composition events whose internal event are not
// WidgetCompositionEvent.
- widgetGUIEvent = aEvent->WidgetEventPtr()->AsCompositionEvent();
+ if (!aGUIEvent->AsCompositionEvent()) {
+ return false;
+ }
needsWidget = true;
break;
default:
break;
}
- if (needsWidget &&
- (!widgetGUIEvent || !widgetGUIEvent->mWidget)) {
+ if (needsWidget && !aGUIEvent->mWidget) {
return false;
}
// Accept all trusted events.
- if (widgetEvent->IsTrusted()) {
+ if (aGUIEvent->IsTrusted()) {
return true;
}
// Ignore untrusted mouse event.
// XXX Why are we handling other untrusted input events?
- if (widgetEvent->AsMouseEventBase()) {
+ if (aGUIEvent->AsMouseEventBase()) {
return false;
}
diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h
index dd4b9695e..dbd00771e 100644
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -247,7 +247,8 @@ public:
* IME event handlers.
*/
virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent);
- virtual nsresult UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) = 0;
+ virtual nsresult UpdateIMEComposition(
+ WidgetCompositionEvent* aCompositionChangeEvent) = 0;
void EndIMEComposition();
void SwitchTextDirectionTo(uint32_t aDirection);
@@ -870,12 +871,12 @@ public:
virtual bool IsActiveInDOMWindow();
/**
- * Whether the aEvent should be handled by this editor or not. When this
- * returns FALSE, The aEvent shouldn't be handled on this editor,
- * i.e., The aEvent should be handled by another inner editor or ancestor
+ * Whether the aGUIEvent should be handled by this editor or not. When this
+ * returns false, The aGUIEvent shouldn't be handled on this editor,
+ * i.e., The aGUIEvent should be handled by another inner editor or ancestor
* elements.
*/
- virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent);
+ virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent);
/**
* FindSelectionRoot() returns a selection root of this editor when aNode
diff --git a/editor/libeditor/EditorEventListener.cpp b/editor/libeditor/EditorEventListener.cpp
index f90458d3e..a6fcfd933 100644
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -214,7 +214,7 @@ EditorEventListener::InstallToEditor()
void
EditorEventListener::Disconnect()
{
- if (!mEditorBase) {
+ if (DetachedFromEditor()) {
return;
}
UninstallFromEditor();
@@ -301,8 +301,7 @@ EditorEventListener::UninstallFromEditor()
already_AddRefed<nsIPresShell>
EditorEventListener::GetPresShell()
{
- NS_PRECONDITION(mEditorBase,
- "The caller must check whether this is connected to an editor");
+ MOZ_ASSERT(!DetachedFromEditor());
return mEditorBase->GetPresShell();
}
@@ -316,8 +315,7 @@ EditorEventListener::GetPresContext()
nsIContent*
EditorEventListener::GetFocusedRootContent()
{
- NS_ENSURE_TRUE(mEditorBase, nullptr);
-
+ MOZ_ASSERT(!DetachedFromEditor());
nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
if (!focusedContent) {
return nullptr;
@@ -336,8 +334,7 @@ EditorEventListener::GetFocusedRootContent()
bool
EditorEventListener::EditorHasFocus()
{
- NS_PRECONDITION(mEditorBase,
- "The caller must check whether this is connected to an editor");
+ MOZ_ASSERT(!DetachedFromEditor());
nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
if (!focusedContent) {
return false;
@@ -348,16 +345,23 @@ EditorEventListener::EditorHasFocus()
NS_IMPL_ISUPPORTS(EditorEventListener, nsIDOMEventListener)
-NS_IMETHODIMP
-EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
+bool
+EditorEventListener::DetachedFromEditor() const
{
- NS_ENSURE_TRUE(mEditorBase, NS_ERROR_FAILURE);
-
- nsCOMPtr<nsIEditor> kungFuDeathGrip = mEditorBase;
- Unused << kungFuDeathGrip; // mEditorBase is not referred to in this function
+ return !mEditorBase;
+}
- WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
+bool
+EditorEventListener::DetachedFromEditorOrDefaultPrevented(
+ WidgetEvent* aWidgetEvent) const
+{
+ return NS_WARN_IF(!aWidgetEvent) || DetachedFromEditor() ||
+ aWidgetEvent->DefaultPrevented();
+}
+NS_IMETHODIMP
+EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
+{
// Let's handle each event with the message of the internal event of the
// coming event. If the DOM event was created with improper interface,
// e.g., keydown event is created with |new MouseEvent("keydown", {});|,
@@ -368,6 +372,7 @@ EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
// calling it, this queries the specific interface. If it would fail,
// each event handler would just ignore the event. So, in this method,
// you don't need to check if the QI succeeded before each call.
+ WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
switch (internalEvent->mMessage) {
// dragenter
case eDragEnter: {
@@ -454,19 +459,19 @@ EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
}
// focus
case eFocus:
- return Focus(aEvent);
+ return Focus(internalEvent);
// blur
case eBlur:
- return Blur(aEvent);
+ return Blur(internalEvent);
// text
case eCompositionChange:
- return HandleText(aEvent);
+ return HandleChangeComposition(internalEvent->AsCompositionEvent());
// compositionstart
case eCompositionStart:
- return HandleStartComposition(aEvent);
+ return HandleStartComposition(internalEvent->AsCompositionEvent());
// compositionend
case eCompositionEnd:
- HandleEndComposition(aEvent);
+ HandleEndComposition(internalEvent->AsCompositionEvent());
return NS_OK;
default:
break;
@@ -477,10 +482,10 @@ EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
// We should accept "focus" and "blur" event even if it's synthesized with
// wrong interface for compatibility with older Gecko.
if (eventType.EqualsLiteral("focus")) {
- return Focus(aEvent);
+ return Focus(internalEvent);
}
if (eventType.EqualsLiteral("blur")) {
- return Blur(aEvent);
+ return Blur(internalEvent);
}
#ifdef DEBUG
nsPrintfCString assertMessage("Editor doesn't handle \"%s\" event "
@@ -541,18 +546,22 @@ bool IsCtrlShiftPressed(nsIDOMKeyEvent* aEvent, bool& isRTL)
nsresult
EditorEventListener::KeyUp(nsIDOMKeyEvent* aKeyEvent)
{
- NS_ENSURE_TRUE(aKeyEvent, NS_OK);
+ if (NS_WARN_IF(!aKeyEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
if (!mHaveBidiKeyboards) {
return NS_OK;
}
+ // XXX Why doesn't this method check if it's consumed?
+ RefPtr<EditorBase> editorBase(mEditorBase);
uint32_t keyCode = 0;
aKeyEvent->GetKeyCode(&keyCode);
if ((keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT ||
keyCode == nsIDOMKeyEvent::DOM_VK_CONTROL) &&
- mShouldSwitchTextDirection && mEditorBase->IsPlaintextEditor()) {
- mEditorBase->SwitchTextDirectionTo(mSwitchToRTL ?
+ mShouldSwitchTextDirection && editorBase->IsPlaintextEditor()) {
+ editorBase->SwitchTextDirectionTo(mSwitchToRTL ?
nsIPlaintextEditor::eEditorRightToLeft :
nsIPlaintextEditor::eEditorLeftToRight);
mShouldSwitchTextDirection = false;
@@ -563,12 +572,15 @@ EditorEventListener::KeyUp(nsIDOMKeyEvent* aKeyEvent)
nsresult
EditorEventListener::KeyDown(nsIDOMKeyEvent* aKeyEvent)
{
- NS_ENSURE_TRUE(aKeyEvent, NS_OK);
+ if (NS_WARN_IF(!aKeyEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
if (!mHaveBidiKeyboards) {
return NS_OK;
}
+ // XXX Why isn't this method check if it's consumed?
uint32_t keyCode = 0;
aKeyEvent->GetKeyCode(&keyCode);
if (keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT) {
@@ -588,29 +600,23 @@ EditorEventListener::KeyDown(nsIDOMKeyEvent* aKeyEvent)
nsresult
EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
{
- NS_ENSURE_TRUE(aKeyEvent, NS_OK);
-
- if (!mEditorBase->IsAcceptableInputEvent(aKeyEvent->AsEvent())) {
+ if (NS_WARN_IF(!aKeyEvent)) {
return NS_OK;
}
- // DOM event handling happens in two passes, the client pass and the system
- // pass. We do all of our processing in the system pass, to allow client
- // handlers the opportunity to cancel events and prevent typing in the editor.
- // If the client pass cancelled the event, defaultPrevented will be true
- // below.
-
- bool defaultPrevented;
- aKeyEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
- if (defaultPrevented) {
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ WidgetKeyboardEvent* keypressEvent =
+ aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
+ MOZ_ASSERT(keypressEvent,
+ "DOM key event's internal event must be WidgetKeyboardEvent");
+ if (!editorBase->IsAcceptableInputEvent(keypressEvent) ||
+ DetachedFromEditorOrDefaultPrevented(keypressEvent)) {
return NS_OK;
}
- nsresult rv = mEditorBase->HandleKeyPressEvent(aKeyEvent);
+ nsresult rv = editorBase->HandleKeyPressEvent(aKeyEvent);
NS_ENSURE_SUCCESS(rv, rv);
-
- aKeyEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
- if (defaultPrevented) {
+ if (DetachedFromEditorOrDefaultPrevented(keypressEvent)) {
return NS_OK;
}
@@ -619,11 +625,7 @@ EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
}
// Now, ask the native key bindings to handle the event.
- WidgetKeyboardEvent* keyEvent =
- aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
- MOZ_ASSERT(keyEvent,
- "DOM key event's internal event must be WidgetKeyboardEvent");
- nsIWidget* widget = keyEvent->mWidget;
+ nsIWidget* widget = keypressEvent->mWidget;
// If the event is created by chrome script, the widget is always nullptr.
if (!widget) {
nsCOMPtr<nsIPresShell> ps = GetPresShell();
@@ -632,10 +634,10 @@ EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
NS_ENSURE_TRUE(widget, NS_OK);
}
- nsCOMPtr<nsIDocument> doc = mEditorBase->GetDocument();
+ nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
bool handled = widget->ExecuteNativeKeyBinding(
nsIWidget::NativeKeyBindingsForRichTextEditor,
- *keyEvent, DoCommandCallback, doc);
+ *keypressEvent, DoCommandCallback, doc);
if (handled) {
aKeyEvent->AsEvent()->PreventDefault();
}
@@ -645,9 +647,15 @@ EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
nsresult
EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
{
+ if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
// nothing to do if editor isn't editable or clicked on out of the editor.
- if (mEditorBase->IsReadonly() || mEditorBase->IsDisabled() ||
- !mEditorBase->IsAcceptableInputEvent(aMouseEvent->AsEvent())) {
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ WidgetMouseEvent* clickEvent =
+ aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
+ if (editorBase->IsReadonly() || editorBase->IsDisabled() ||
+ !editorBase->IsAcceptableInputEvent(clickEvent)) {
return NS_OK;
}
@@ -658,26 +666,23 @@ EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
if (presContext) {
IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
aMouseEvent);
- }
+ if (DetachedFromEditor()) {
+ return NS_OK;
+ }
+ }
}
- bool preventDefault;
- nsresult rv = aMouseEvent->AsEvent()->GetDefaultPrevented(&preventDefault);
- if (NS_FAILED(rv) || preventDefault) {
+ if (DetachedFromEditorOrDefaultPrevented(clickEvent)) {
// We're done if 'preventdefault' is true (see for example bug 70698).
- return rv;
- }
-
- // IMEStateManager::OnClickInEditor() may cause anything because it may
- // set input context. For example, it may cause opening VKB, changing focus
- // or reflow. So, mEditorBase here might have been gone.
- if (!mEditorBase) {
return NS_OK;
}
// If we got a mouse down inside the editing area, we should force the
// IME to commit before we change the cursor position
- mEditorBase->ForceCompositionEnd();
+ editorBase->ForceCompositionEnd();
+ if (DetachedFromEditor()) {
+ return NS_OK;
+ }
int16_t button = -1;
aMouseEvent->GetButton(&button);
@@ -690,6 +695,10 @@ EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
nsresult
EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
{
+ MOZ_ASSERT(aMouseEvent);
+ MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(
+ aMouseEvent->AsEvent()->WidgetEventPtr()));
+
if (!Preferences::GetBool("middlemouse.paste", false)) {
// Middle click paste isn't enabled.
return NS_OK;
@@ -705,7 +714,8 @@ EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
return NS_ERROR_NULL_POINTER;
}
- RefPtr<Selection> selection = mEditorBase->GetSelection();
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ RefPtr<Selection> selection = editorBase->GetSelection();
if (selection) {
selection->Collapse(parent, offset);
}
@@ -717,7 +727,7 @@ EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
nsCOMPtr<nsIEditorMailSupport> mailEditor;
if (ctrlKey) {
- mailEditor = do_QueryObject(mEditorBase);
+ mailEditor = do_QueryObject(editorBase);
}
nsresult rv;
@@ -735,7 +745,7 @@ EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
if (mailEditor) {
mailEditor->PasteAsQuotation(clipboard);
} else {
- mEditorBase->Paste(clipboard);
+ editorBase->Paste(clipboard);
}
// Prevent the event from propagating up to be possibly handled
@@ -751,16 +761,12 @@ bool
EditorEventListener::NotifyIMEOfMouseButtonEvent(
nsIDOMMouseEvent* aMouseEvent)
{
+ MOZ_ASSERT(aMouseEvent);
+
if (!EditorHasFocus()) {
return false;
}
- bool defaultPrevented;
- nsresult rv = aMouseEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
- NS_ENSURE_SUCCESS(rv, false);
- if (defaultPrevented) {
- return false;
- }
nsPresContext* presContext = GetPresContext();
NS_ENSURE_TRUE(presContext, false);
return IMEStateManager::OnMouseButtonEventInEditor(presContext,
@@ -771,27 +777,36 @@ EditorEventListener::NotifyIMEOfMouseButtonEvent(
nsresult
EditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
{
+ // FYI: We don't need to check if it's already consumed here because
+ // we need to commit composition at mouse button operation.
// FYI: This may be called by HTMLEditorEventListener::MouseDown() even
// when the event is not acceptable for committing composition.
- if (mEditorBase) {
- mEditorBase->ForceCompositionEnd();
+ if (DetachedFromEditor()) {
+ return NS_OK;
}
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ editorBase->ForceCompositionEnd();
return NS_OK;
}
nsresult
-EditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
+EditorEventListener::HandleChangeComposition(
+ WidgetCompositionEvent* aCompositionChangeEvent)
{
- if (!mEditorBase->IsAcceptableInputEvent(aTextEvent)) {
+ MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
+ "eCompositionChange event shouldn't be cancelable");
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ if (DetachedFromEditor() ||
+ !editorBase->IsAcceptableInputEvent(aCompositionChangeEvent)) {
return NS_OK;
}
// if we are readonly or disabled, then do nothing.
- if (mEditorBase->IsReadonly() || mEditorBase->IsDisabled()) {
+ if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
return NS_OK;
}
- return mEditorBase->UpdateIMEComposition(aTextEvent);
+ return editorBase->UpdateIMEComposition(aCompositionChangeEvent);
}
/**
@@ -801,7 +816,9 @@ EditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
nsresult
EditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
{
- NS_ENSURE_TRUE(aDragEvent, NS_OK);
+ if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
NS_ENSURE_TRUE(presShell, NS_OK);
@@ -824,15 +841,13 @@ EditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
nsresult
EditorEventListener::DragOver(nsIDOMDragEvent* aDragEvent)
{
- NS_ENSURE_TRUE(aDragEvent, NS_OK);
-
- nsCOMPtr<nsIDOMNode> parent;
- bool defaultPrevented;
- aDragEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
- if (defaultPrevented) {
+ if (NS_WARN_IF(!aDragEvent) ||
+ DetachedFromEditorOrDefaultPrevented(
+ aDragEvent->AsEvent()->WidgetEventPtr())) {
return NS_OK;
}
+ nsCOMPtr<nsIDOMNode> parent;
aDragEvent->GetRangeParent(getter_AddRefs(parent));
nsCOMPtr<nsIContent> dropParent = do_QueryInterface(parent);
NS_ENSURE_TRUE(dropParent, NS_ERROR_FAILURE);
@@ -887,7 +902,15 @@ EditorEventListener::CleanupDragDropCaret()
nsresult
EditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
{
- NS_ENSURE_TRUE(aDragEvent, NS_OK);
+ // XXX If aDragEvent was created by chrome script, its defaultPrevented
+ // may be true, though. We shouldn't handle such event but we don't
+ // have a way to distinguish if coming event is created by chrome script.
+ NS_WARNING_ASSERTION(
+ !aDragEvent->AsEvent()->WidgetEventPtr()->DefaultPrevented(),
+ "eDragExit shouldn't be cancelable");
+ if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
CleanupDragDropCaret();
@@ -897,13 +920,11 @@ EditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
nsresult
EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
{
- NS_ENSURE_TRUE(aDragEvent, NS_OK);
-
CleanupDragDropCaret();
- bool defaultPrevented;
- aDragEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
- if (defaultPrevented) {
+ if (NS_WARN_IF(!aDragEvent) ||
+ DetachedFromEditorOrDefaultPrevented(
+ aDragEvent->AsEvent()->WidgetEventPtr())) {
return NS_OK;
}
@@ -914,7 +935,8 @@ EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
if (!dropParent->IsEditable() || !CanDrop(aDragEvent)) {
// was it because we're read-only?
- if ((mEditorBase->IsReadonly() || mEditorBase->IsDisabled()) &&
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ if ((editorBase->IsReadonly() || editorBase->IsDisabled()) &&
!IsFileControlTextBox()) {
// it was decided to "eat" the event as this is the "least surprise"
// since someone else handling it might be unintentional and the
@@ -927,14 +949,19 @@ EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
aDragEvent->AsEvent()->StopPropagation();
aDragEvent->AsEvent()->PreventDefault();
- return mEditorBase->InsertFromDrop(aDragEvent->AsEvent());
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ return editorBase->InsertFromDrop(aDragEvent->AsEvent());
}
bool
EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
{
+ MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(
+ aEvent->AsEvent()->WidgetEventPtr()));
+
// if the target doc is read-only, we can't drop
- if (mEditorBase->IsReadonly() || mEditorBase->IsDisabled()) {
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
return false;
}
@@ -950,7 +977,7 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
// can be dropped as well.
if (!types.Contains(NS_LITERAL_STRING(kTextMime)) &&
!types.Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
- (mEditorBase->IsPlaintextEditor() ||
+ (editorBase->IsPlaintextEditor() ||
(!types.Contains(NS_LITERAL_STRING(kHTMLMime)) &&
!types.Contains(NS_LITERAL_STRING(kFileMime))))) {
return false;
@@ -968,7 +995,7 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
// There is a source node, so compare the source documents and this document.
// Disallow drops on the same document.
- nsCOMPtr<nsIDOMDocument> domdoc = mEditorBase->GetDOMDocument();
+ nsCOMPtr<nsIDOMDocument> domdoc = editorBase->GetDOMDocument();
NS_ENSURE_TRUE(domdoc, false);
nsCOMPtr<nsIDOMDocument> sourceDoc;
@@ -988,7 +1015,7 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
return true;
}
- RefPtr<Selection> selection = mEditorBase->GetSelection();
+ RefPtr<Selection> selection = editorBase->GetSelection();
if (!selection) {
return false;
}
@@ -1030,46 +1057,63 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
}
nsresult
-EditorEventListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
+EditorEventListener::HandleStartComposition(
+ WidgetCompositionEvent* aCompositionStartEvent)
{
- if (!mEditorBase->IsAcceptableInputEvent(aCompositionEvent)) {
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ if (DetachedFromEditor() ||
+ !editorBase->IsAcceptableInputEvent(aCompositionStartEvent)) {
return NS_OK;
}
- WidgetCompositionEvent* compositionStart =
- aCompositionEvent->WidgetEventPtr()->AsCompositionEvent();
- return mEditorBase->BeginIMEComposition(compositionStart);
+ // Although, "compositionstart" should be cancelable, but currently,
+ // eCompositionStart event coming from widget is not cancelable.
+ MOZ_ASSERT(!aCompositionStartEvent->DefaultPrevented(),
+ "eCompositionStart shouldn't be cancelable");
+ return editorBase->BeginIMEComposition(aCompositionStartEvent);
}
void
-EditorEventListener::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
+EditorEventListener::HandleEndComposition(
+ WidgetCompositionEvent* aCompositionEndEvent)
{
- if (!mEditorBase->IsAcceptableInputEvent(aCompositionEvent)) {
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ if (DetachedFromEditor() ||
+ !editorBase->IsAcceptableInputEvent(aCompositionEndEvent)) {
return;
}
-
- mEditorBase->EndIMEComposition();
+ MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
+ "eCompositionEnd shouldn't be cancelable");
+ editorBase->EndIMEComposition();
}
nsresult
-EditorEventListener::Focus(nsIDOMEvent* aEvent)
+EditorEventListener::Focus(WidgetEvent* aFocusEvent)
{
- NS_ENSURE_TRUE(aEvent, NS_OK);
+ if (NS_WARN_IF(!aFocusEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
+
+ // XXX If aFocusEvent was created by chrome script, its defaultPrevented
+ // may be true, though. We shouldn't handle such event but we don't
+ // have a way to distinguish if coming event is created by chrome script.
+ NS_WARNING_ASSERTION(!aFocusEvent->DefaultPrevented(),
+ "eFocus event shouldn't be cancelable");
// Don't turn on selection and caret when the editor is disabled.
- if (mEditorBase->IsDisabled()) {
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ if (editorBase->IsDisabled()) {
return NS_OK;
}
// Spell check a textarea the first time that it is focused.
SpellCheckIfNeeded();
- if (!mEditorBase) {
+ if (!editorBase) {
// In e10s, this can cause us to flush notifications, which can destroy
// the node we're about to focus.
return NS_OK;
}
- nsCOMPtr<nsIDOMEventTarget> target;
- aEvent->GetTarget(getter_AddRefs(target));
+ nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetDOMEventTarget();
nsCOMPtr<nsINode> node = do_QueryInterface(target);
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
@@ -1087,7 +1131,7 @@ EditorEventListener::Focus(nsIDOMEvent* aEvent)
// contenteditable editor. So, the editableRoot value is invalid for
// the plain text editor, and it will be set to the wrong limiter of
// the selection. However, fortunately, actual bugs are not found yet.
- nsCOMPtr<nsIContent> editableRoot = mEditorBase->FindSelectionRoot(node);
+ nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(node);
// make sure that the element is really focused in case an earlier
// listener in the chain changed the focus.
@@ -1101,8 +1145,8 @@ EditorEventListener::Focus(nsIDOMEvent* aEvent)
return NS_OK;
}
- nsCOMPtr<nsIDOMEventTarget> originalTarget;
- aEvent->GetOriginalTarget(getter_AddRefs(originalTarget));
+ nsCOMPtr<nsIDOMEventTarget> originalTarget =
+ aFocusEvent->GetOriginalDOMEventTarget();
nsCOMPtr<nsIContent> originalTargetAsContent =
do_QueryInterface(originalTarget);
@@ -1117,21 +1161,32 @@ EditorEventListener::Focus(nsIDOMEvent* aEvent)
}
}
- mEditorBase->OnFocus(target);
+ editorBase->OnFocus(target);
+ if (DetachedFromEditorOrDefaultPrevented(aFocusEvent)) {
+ return NS_OK;
+ }
nsCOMPtr<nsIPresShell> ps = GetPresShell();
NS_ENSURE_TRUE(ps, NS_OK);
- nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContentForIME();
+ nsCOMPtr<nsIContent> focusedContent = editorBase->GetFocusedContentForIME();
IMEStateManager::OnFocusInEditor(ps->GetPresContext(), focusedContent,
- mEditorBase);
+ editorBase);
return NS_OK;
}
nsresult
-EditorEventListener::Blur(nsIDOMEvent* aEvent)
+EditorEventListener::Blur(WidgetEvent* aBlurEvent)
{
- NS_ENSURE_TRUE(aEvent, NS_OK);
+ if (NS_WARN_IF(!aBlurEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
+
+ // XXX If aBlurEvent was created by chrome script, its defaultPrevented
+ // may be true, though. We shouldn't handle such event but we don't
+ // have a way to distinguish if coming event is created by chrome script.
+ NS_WARNING_ASSERTION(!aBlurEvent->DefaultPrevented(),
+ "eBlur event shouldn't be cancelable");
// check if something else is focused. If another element is focused, then
// we should not change the selection.
@@ -1141,7 +1196,8 @@ EditorEventListener::Blur(nsIDOMEvent* aEvent)
nsCOMPtr<nsIDOMElement> element;
fm->GetFocusedElement(getter_AddRefs(element));
if (!element) {
- mEditorBase->FinalizeSelection();
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ editorBase->FinalizeSelection();
}
return NS_OK;
}
@@ -1149,20 +1205,26 @@ EditorEventListener::Blur(nsIDOMEvent* aEvent)
void
EditorEventListener::SpellCheckIfNeeded()
{
+ MOZ_ASSERT(!DetachedFromEditor());
+
// If the spell check skip flag is still enabled from creation time,
// disable it because focused editors are allowed to spell check.
+ RefPtr<EditorBase> editorBase(mEditorBase);
uint32_t currentFlags = 0;
- mEditorBase->GetFlags(&currentFlags);
+ editorBase->GetFlags(&currentFlags);
if(currentFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) {
currentFlags ^= nsIPlaintextEditor::eEditorSkipSpellCheck;
- mEditorBase->SetFlags(currentFlags);
+ editorBase->SetFlags(currentFlags);
}
}
bool
EditorEventListener::IsFileControlTextBox()
{
- Element* root = mEditorBase->GetRoot();
+ MOZ_ASSERT(!DetachedFromEditor());
+
+ RefPtr<EditorBase> editorBase(mEditorBase);
+ Element* root = editorBase->GetRoot();
if (!root || !root->ChromeOnlyAccess()) {
return false;
}
@@ -1177,6 +1239,8 @@ EditorEventListener::IsFileControlTextBox()
bool
EditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent)
{
+ MOZ_ASSERT(!DetachedFromEditor());
+
// Only return true if the target of the event is a desendant of the active
// editing host in order to match the similar decision made in
// nsXBLWindowKeyHandler.
@@ -1192,13 +1256,14 @@ EditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent)
return false;
}
+ RefPtr<EditorBase> editorBase(mEditorBase);
nsCOMPtr<nsIHTMLEditor> htmlEditor =
- do_QueryInterface(static_cast<nsIEditor*>(mEditorBase));
+ do_QueryInterface(static_cast<nsIEditor*>(editorBase));
if (!htmlEditor) {
return false;
}
- nsCOMPtr<nsIDocument> doc = mEditorBase->GetDocument();
+ nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
if (doc->HasFlag(NODE_IS_EDITABLE)) {
// Don't need to perform any checks in designMode documents.
return true;
diff --git a/editor/libeditor/EditorEventListener.h b/editor/libeditor/EditorEventListener.h
index 505b711c7..7244ebea4 100644
--- a/editor/libeditor/EditorEventListener.h
+++ b/editor/libeditor/EditorEventListener.h
@@ -6,6 +6,7 @@
#ifndef EditorEventListener_h
#define EditorEventListener_h
+#include "mozilla/EventForwards.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsIDOMEventListener.h"
@@ -60,14 +61,14 @@ protected:
nsresult KeyUp(nsIDOMKeyEvent* aKeyEvent);
#endif
nsresult KeyPress(nsIDOMKeyEvent* aKeyEvent);
- nsresult HandleText(nsIDOMEvent* aTextEvent);
- nsresult HandleStartComposition(nsIDOMEvent* aCompositionEvent);
- void HandleEndComposition(nsIDOMEvent* aCompositionEvent);
+ nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent);
+ nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent);
+ void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent);
virtual nsresult MouseDown(nsIDOMMouseEvent* aMouseEvent);
virtual nsresult MouseUp(nsIDOMMouseEvent* aMouseEvent) { return NS_OK; }
virtual nsresult MouseClick(nsIDOMMouseEvent* aMouseEvent);
- nsresult Focus(nsIDOMEvent* aEvent);
- nsresult Blur(nsIDOMEvent* aEvent);
+ nsresult Focus(WidgetEvent* aFocusEvent);
+ nsresult Blur(WidgetEvent* aBlurEvent);
nsresult DragEnter(nsIDOMDragEvent* aDragEvent);
nsresult DragOver(nsIDOMDragEvent* aDragEvent);
nsresult DragExit(nsIDOMDragEvent* aDragEvent);
@@ -85,6 +86,19 @@ protected:
bool ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent);
nsresult HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent);
+ /**
+ * DetachedFromEditor() returns true if editor was detached.
+ * Otherwise, false.
+ */
+ bool DetachedFromEditor() const;
+
+ /**
+ * DetachedFromEditorOrDefaultPrevented() returns true if editor was detached
+ * and/or the event was consumed. Otherwise, i.e., attached editor can
+ * handle the event, returns true.
+ */
+ bool DetachedFromEditorOrDefaultPrevented(WidgetEvent* aEvent) const;
+
EditorBase* mEditorBase; // weak
RefPtr<nsCaret> mCaret;
bool mCommitText;
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
index dd47ffd3c..368f7a3e9 100644
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -5161,23 +5161,22 @@ HTMLEditor::OurWindowHasFocus()
}
bool
-HTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
+HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
{
- if (!EditorBase::IsAcceptableInputEvent(aEvent)) {
+ if (!EditorBase::IsAcceptableInputEvent(aGUIEvent)) {
return false;
}
// While there is composition, all composition events in its top level window
// are always fired on the composing editor. Therefore, if this editor has
// composition, the composition events should be handled in this editor.
- if (mComposition && aEvent->WidgetEventPtr()->AsCompositionEvent()) {
+ if (mComposition && aGUIEvent->AsCompositionEvent()) {
return true;
}
NS_ENSURE_TRUE(mDocWeak, false);
- nsCOMPtr<nsIDOMEventTarget> target;
- aEvent->GetTarget(getter_AddRefs(target));
+ nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetDOMEventTarget();
NS_ENSURE_TRUE(target, false);
nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak);
@@ -5201,8 +5200,7 @@ HTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
// If the event is a mouse event, we need to check if the target content is
// the focused editing host or its descendant.
- nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
- if (mouseEvent) {
+ if (aGUIEvent->AsMouseEventBase()) {
nsIContent* editingHost = GetActiveEditingHost();
// If there is no active editing host, we cannot handle the mouse event
// correctly.
diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h
index 477ec9741..dfcdd8d6b 100644
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -114,7 +114,7 @@ public:
virtual Element* GetEditorRoot() override;
virtual already_AddRefed<nsIContent> FindSelectionRoot(
nsINode *aNode) override;
- virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent) override;
+ virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) override;
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override;
virtual bool IsEditable(nsINode* aNode) override;
using EditorBase::IsEditable;
diff --git a/editor/libeditor/HTMLEditorEventListener.cpp b/editor/libeditor/HTMLEditorEventListener.cpp
index 8fb9459c2..aa767519c 100644
--- a/editor/libeditor/HTMLEditorEventListener.cpp
+++ b/editor/libeditor/HTMLEditorEventListener.cpp
@@ -7,6 +7,7 @@
#include "HTMLEditUtils.h"
#include "mozilla/HTMLEditor.h"
+#include "mozilla/MouseEvents.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Selection.h"
#include "nsCOMPtr.h"
@@ -52,6 +53,12 @@ HTMLEditorEventListener::GetHTMLEditor()
nsresult
HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
{
+ if (DetachedFromEditor()) {
+ return NS_OK;
+ }
+
+ // FYI: We need to notify HTML editor of mouseup even if it's consumed
+ // because HTML editor always needs to release grabbing resizer.
HTMLEditor* htmlEditor = GetHTMLEditor();
nsCOMPtr<nsIDOMEventTarget> target;
@@ -71,10 +78,17 @@ HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
nsresult
HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
{
+ if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
+ return NS_OK;
+ }
+
+ WidgetMouseEvent* mousedownEvent =
+ aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
+
HTMLEditor* htmlEditor = GetHTMLEditor();
// Contenteditable should disregard mousedowns outside it.
// IsAcceptableInputEvent() checks it for a mouse event.
- if (!htmlEditor->IsAcceptableInputEvent(aMouseEvent->AsEvent())) {
+ if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) {
// If it's not acceptable mousedown event (including when mousedown event
// is fired outside of the active editing host), we need to commit
// composition because it will be change the selection to the clicked
@@ -82,6 +96,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
return EditorEventListener::MouseDown(aMouseEvent);
}
+ // XXX This method may change selection. So, we need to commit composition
+ // here, first.
+
// Detect only "context menu" click
// XXX This should be easier to do!
// But eDOMEvents_contextmenu and eContextMenu is not exposed in any event
@@ -103,7 +120,7 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
if (isContextClick || (buttonNumber == 0 && clickCount == 2)) {
- RefPtr<Selection> selection = mEditorBase->GetSelection();
+ RefPtr<Selection> selection = htmlEditor->GetSelection();
NS_ENSURE_TRUE(selection, NS_OK);
// Get location of mouse within target node
@@ -175,6 +192,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
} else {
htmlEditor->SelectElement(element);
}
+ if (DetachedFromEditor()) {
+ return NS_OK;
+ }
}
}
// HACK !!! Context click places the caret but the context menu consumes
diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp
index 8fe824e11..d21585597 100644
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -834,17 +834,19 @@ TextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent)
}
nsresult
-TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
+TextEditor::UpdateIMEComposition(WidgetCompositionEvent* aCompositionChangeEvent)
{
- MOZ_ASSERT(aDOMTextEvent, "aDOMTextEvent must not be nullptr");
+ MOZ_ASSERT(aCompsitionChangeEvent,
+ "aCompositionChangeEvent must not be nullptr");
- WidgetCompositionEvent* compositionChangeEvent =
- aDOMTextEvent->WidgetEventPtr()->AsCompositionEvent();
- NS_ENSURE_TRUE(compositionChangeEvent, NS_ERROR_INVALID_ARG);
- MOZ_ASSERT(compositionChangeEvent->mMessage == eCompositionChange,
- "The internal event should be eCompositionChange");
+ if (NS_WARN_IF(!aCompositionChangeEvent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ MOZ_ASSERT(aCompositionChangeEvent->mMessage == eCompositionChange,
+ "The event should be eCompositionChange");
- if (!EnsureComposition(compositionChangeEvent)) {
+ if (!EnsureComposition(aCompositionChangeEvent)) {
return NS_OK;
}
@@ -865,7 +867,7 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
MOZ_ASSERT(!mPlaceHolderBatch,
"UpdateIMEComposition() must be called without place holder batch");
TextComposition::CompositionChangeEventHandlingMarker
- compositionChangeEventHandlingMarker(mComposition, compositionChangeEvent);
+ compositionChangeEventHandlingMarker(mComposition, aCompositionChangeEvent);
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
@@ -875,7 +877,7 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
{
AutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
- rv = InsertText(compositionChangeEvent->mData);
+ rv = InsertText(aCompositionChangeEvent->mData);
if (caretP) {
caretP->SetSelection(selection);
@@ -887,7 +889,7 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
// compositionend event, we don't need to notify editor observes of this
// change.
// NOTE: We must notify after the auto batch will be gone.
- if (!compositionChangeEvent->IsFollowedByCompositionEnd()) {
+ if (!aCompositionChangeEvent->IsFollowedByCompositionEnd()) {
NotifyEditorObservers(eNotifyEditorObserversOfEnd);
}
diff --git a/editor/libeditor/TextEditor.h b/editor/libeditor/TextEditor.h
index 872cd91d3..31c551f85 100644
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -130,7 +130,8 @@ public:
virtual already_AddRefed<dom::EventTarget> GetDOMEventTarget() override;
virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent) override;
- virtual nsresult UpdateIMEComposition(nsIDOMEvent* aTextEvent) override;
+ virtual nsresult UpdateIMEComposition(
+ WidgetCompositionEvent* aCompositionChangeEvent) override;
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override;