diff options
Diffstat (limited to 'editor')
25 files changed, 532 insertions, 205 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(¤tFlags); + editorBase->GetFlags(¤tFlags); 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/HTMLEditorDataTransfer.cpp b/editor/libeditor/HTMLEditorDataTransfer.cpp index b9cd8adb9..c56fbead7 100644 --- a/editor/libeditor/HTMLEditorDataTransfer.cpp +++ b/editor/libeditor/HTMLEditorDataTransfer.cpp @@ -1538,6 +1538,13 @@ HTMLEditor::CanPaste(int32_t aSelectionType, NS_ENSURE_ARG_POINTER(aCanPaste); *aCanPaste = false; + // Always enable the paste command when inside of a HTML or XHTML document. + nsCOMPtr<nsIDocument> doc = GetDocument(); + if (doc && doc->IsHTMLOrXHTML()) { + *aCanPaste = true; + return NS_OK; + } + // can't paste if readonly if (!IsModifiable()) { return NS_OK; @@ -2382,27 +2389,26 @@ HTMLEditor::ReplaceOrphanedStructure( } // If we found substructure, paste it instead of its descendants. - // Only replace with the substructure if all the nodes in the list are - // descendants. - bool shouldReplaceNodes = true; - for (uint32_t i = 0; i < aNodeArray.Length(); i++) { + // Postprocess list to remove any descendants of this node so that we don't + // insert them twice. + uint32_t removedCount = 0; + uint32_t originalLength = aNodeArray.Length(); + for (uint32_t i = 0; i < originalLength; i++) { uint32_t idx = aStartOrEnd == StartOrEnd::start ? - i : (aNodeArray.Length() - i - 1); + (i - removedCount) : (originalLength - i - 1); OwningNonNull<nsINode> endpoint = aNodeArray[idx]; - if (!EditorUtils::IsDescendantOf(endpoint, replaceNode)) { - shouldReplaceNodes = false; - break; + if (endpoint == replaceNode || + EditorUtils::IsDescendantOf(endpoint, replaceNode)) { + aNodeArray.RemoveElementAt(idx); + removedCount++; } } - if (shouldReplaceNodes) { - // Now replace the removed nodes with the structural parent - aNodeArray.Clear(); - if (aStartOrEnd == StartOrEnd::end) { - aNodeArray.AppendElement(*replaceNode); - } else { - aNodeArray.InsertElementAt(0, *replaceNode); - } + // Now replace the removed nodes with the structural parent + if (aStartOrEnd == StartOrEnd::end) { + aNodeArray.AppendElement(*replaceNode); + } else { + aNodeArray.InsertElementAt(0, *replaceNode); } } 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; diff --git a/editor/libeditor/TextEditorDataTransfer.cpp b/editor/libeditor/TextEditorDataTransfer.cpp index 0388aa4a8..2cc2906fa 100644 --- a/editor/libeditor/TextEditorDataTransfer.cpp +++ b/editor/libeditor/TextEditorDataTransfer.cpp @@ -383,6 +383,13 @@ TextEditor::CanPaste(int32_t aSelectionType, NS_ENSURE_ARG_POINTER(aCanPaste); *aCanPaste = false; + // Always enable the paste command when inside of a HTML or XHTML document. + nsCOMPtr<nsIDocument> doc = GetDocument(); + if (doc && doc->IsHTMLOrXHTML()) { + *aCanPaste = true; + return NS_OK; + } + // can't paste if readonly if (!IsModifiable()) { return NS_OK; diff --git a/editor/libeditor/tests/browser.ini b/editor/libeditor/tests/browser.ini index 249f59aa8..bd234fc8d 100644 --- a/editor/libeditor/tests/browser.ini +++ b/editor/libeditor/tests/browser.ini @@ -1,6 +1,8 @@ [browser_bug527935.js] skip-if = toolkit == 'android' -support-files = bug527935.html +support-files = + bug527935.html + bug527935_2.html [browser_bug629172.js] skip-if = toolkit == 'android' support-files = bug629172.html diff --git a/editor/libeditor/tests/bug527935.html b/editor/libeditor/tests/bug527935.html index 4bfa1bac2..1731734d2 100644 --- a/editor/libeditor/tests/bug527935.html +++ b/editor/libeditor/tests/bug527935.html @@ -3,7 +3,7 @@ <body> <div id="content"> <iframe id="formTarget" name="formTarget"></iframe> - <form action="data:text/html," target="formTarget"> + <form action="bug527935_2.html" target="formTarget"> <input name="test" id="initValue"><input type="submit"> </form> </div> diff --git a/editor/libeditor/tests/bug527935_2.html b/editor/libeditor/tests/bug527935_2.html new file mode 100644 index 000000000..96af0721d --- /dev/null +++ b/editor/libeditor/tests/bug527935_2.html @@ -0,0 +1 @@ +<html><body>dummy page</body></html> diff --git a/editor/libeditor/tests/chrome.ini b/editor/libeditor/tests/chrome.ini index 98db30001..dd13370a5 100644 --- a/editor/libeditor/tests/chrome.ini +++ b/editor/libeditor/tests/chrome.ini @@ -12,3 +12,4 @@ support-files = green.png [test_set_document_title_transaction.html] [test_texteditor_keyevent_handling.html] skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux +[test_pasteImgTextarea.xul] diff --git a/editor/libeditor/tests/file_bug635636_2.html b/editor/libeditor/tests/file_bug635636_2.html new file mode 100644 index 000000000..bf0c8101f --- /dev/null +++ b/editor/libeditor/tests/file_bug635636_2.html @@ -0,0 +1 @@ +<html><body>2</body></html> diff --git a/editor/libeditor/tests/file_bug966155.html b/editor/libeditor/tests/file_bug966155.html new file mode 100644 index 000000000..04f55a918 --- /dev/null +++ b/editor/libeditor/tests/file_bug966155.html @@ -0,0 +1 @@ +<input><iframe onload="contentDocument.designMode = 'on';"> diff --git a/editor/libeditor/tests/file_bug966552.html b/editor/libeditor/tests/file_bug966552.html new file mode 100644 index 000000000..5061c2e40 --- /dev/null +++ b/editor/libeditor/tests/file_bug966552.html @@ -0,0 +1 @@ +<body onload="document.designMode='on'">test</body> diff --git a/editor/libeditor/tests/mochitest.ini b/editor/libeditor/tests/mochitest.ini index 447fb8b65..33b164819 100644 --- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -217,6 +217,9 @@ skip-if = toolkit == 'android' [test_bug1258085.html] [test_bug1268736.html] [test_bug1270235.html] +[test_bug1306532.html] +subsuite = clipboard +skip-if = toolkit == 'android' [test_bug1310912.html] skip-if = toolkit == 'android' # bug 1315898 [test_bug1314790.html] @@ -224,6 +227,7 @@ skip-if = toolkit == 'android' # bug 1315898 [test_bug1328023.html] [test_bug1330796.html] [test_bug1332876.html] +[test_bug1352799.html] [test_CF_HTML_clipboard.html] subsuite = clipboard @@ -243,3 +247,5 @@ skip-if = toolkit == 'android' [test_css_chrome_load_access.html] skip-if = toolkit == 'android' # chrome urls not available due to packaging [test_selection_move_commands.html] +[test_pasteImgTextarea.html] +skip-if = toolkit == 'android' # bug 1299578 diff --git a/editor/libeditor/tests/test_bug1306532.html b/editor/libeditor/tests/test_bug1306532.html new file mode 100644 index 000000000..1d7b3e7af --- /dev/null +++ b/editor/libeditor/tests/test_bug1306532.html @@ -0,0 +1,64 @@ +<!DOCTYPE HTML> +<html><head> +<title>Test for bug 1306532</title> +<style src="/tests/SimpleTest/test.css" type="text/css"></style> +<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + +<script class="testbody" type="application/javascript"> + +function runTest() { + // Copy content from table. + var selection = getSelection(); + var startRange = document.createRange(); + startRange.setStart(headingone, 0); + startRange.setEnd(celltwo, 0); + selection.removeAllRanges(); + selection.addRange(startRange); + SpecialPowers.wrap(document).execCommand("copy", false, null); + + // Paste content into "pasteframe" + var pasteContainer = pasteframe.contentDocument.body; + var pasteRange = pasteframe.contentDocument.createRange(); + pasteRange.selectNodeContents(pasteContainer); + pasteRange.collapse(false); + selection.removeAllRanges(); + selection.addRange(pasteRange); + SpecialPowers.wrap(pasteframe.contentDocument).execCommand("paste", false, null); + + is(pasteContainer.querySelector("#headingone").textContent, "Month", "First heading should be 'Month'."); + is(pasteContainer.querySelector("#headingtwo").textContent, "Savings", "Second heading should be 'Savings'."); + is(pasteContainer.querySelector("#cellone").textContent, "January", "First cell should be 'January'."); + is(pasteContainer.querySelector("#celltwo").textContent, "$100", "Second cell should be '$100'."); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1306532">Mozilla Bug 1306532</a> +<p id="display"></p> + +<pre id="test"> +</pre> + +<div id="container"> +<table border="1"> + <tr> + <th id="headingone">Month</th> + <th id="headingtwo">Savings</th> + </tr> + <tr> + <td id="cellone">January</td> + <td id="celltwo">$100</td> + </tr> +</table> +</div> + +<iframe onload="runTest();" id="pasteframe" src="data:text/html,<html><body contenteditable='true'>"></iframe> + +</body> +</html> diff --git a/editor/libeditor/tests/test_bug1352799.html b/editor/libeditor/tests/test_bug1352799.html new file mode 100644 index 000000000..daedc40fc --- /dev/null +++ b/editor/libeditor/tests/test_bug1352799.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1352799
+-->
+<head>
+ <title>Test for Bug 1352799</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1352799">Mozilla Bug 1352799</a>
+<p id="display"></p>
+<div id="content">
+<div id="input-container" style="display: none;">
+<input id="input" maxlength="1">
+</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1352799 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ var input = document.getElementById("input");
+
+ var inputcontainer = document.getElementById('input-container');
+ input.setAttribute('maxlength', 2);
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('1', {});
+ synthesizeKey('2', {});
+ synthesizeKey('3', {});
+
+ is(input.value, '12', 'value should be 12 with maxlength = 2');
+
+ input.value = '';
+ inputcontainer.style.display = 'none';
+
+ window.setTimeout(() => {
+ input.setAttribute('maxlength', 4);
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('4', {});
+ synthesizeKey('5', {});
+ synthesizeKey('6', {});
+ synthesizeKey('7', {});
+ synthesizeKey('8', {});
+
+ is(input.value, '4567', 'value should be 4567 with maxlength = 4');
+
+ inputcontainer.style.display = 'none';
+
+ window.setTimeout(() => {
+ input.setAttribute('maxlength', 2);
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('1', {});
+ synthesizeKey('2', {});
+
+ todo_is(input.value, '45', 'value should be 45 with maxlength = 2');
+
+ input.value = '';
+ inputcontainer.style.display = 'none';
+
+ window.setTimeout(() => {
+ input.removeAttribute('maxlength');
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('1', {});
+ synthesizeKey('2', {});
+ synthesizeKey('3', {});
+ synthesizeKey('4', {});
+ synthesizeKey('5', {});
+ synthesizeKey('6', {});
+ synthesizeKey('7', {});
+ synthesizeKey('8', {});
+
+ is(input.value, '12345678', 'value should be 12345678 without maxlength');
+
+ SimpleTest.finish();
+ }, 0);
+ }, 0);
+ }, 0);
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/libeditor/tests/test_bug966155.html b/editor/libeditor/tests/test_bug966155.html index 524b15d69..1e7ed67f3 100644 --- a/editor/libeditor/tests/test_bug966155.html +++ b/editor/libeditor/tests/test_bug966155.html @@ -18,13 +18,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=966155 <script class="testbody" type="text/javascript"> SimpleTest.waitForExplicitFinish(); -addLoadEvent(function() { - var win = window.open("data:text/html,<input><iframe onload=\"contentDocument.designMode = 'on';\">", "", "test-966155"); - win.addEventListener("load", function onLoad() { - win.removeEventListener("load", onLoad); - runTest(win); - }, false); -}); +var win = window.open("file_bug966155.html", "", "test-966155"); +win.addEventListener("load", function() { + runTest(win); +}, {once: true}); function runTest(win) { SimpleTest.waitForFocus(function() { @@ -38,14 +35,14 @@ function runTest(win) { "test".split("").forEach(function(letter) { synthesizeKey(letter, {}, win); }); - is(iframeDoc.body.textContent, "test", "entered the text"); + is(iframeDoc.body.textContent.trim(), "test", "entered the text"); // focus the input box input.focus(); // press tab synthesizeKey("VK_TAB", {}, win); // Now press Ctrl+Backspace synthesizeKey("VK_BACK_SPACE", {ctrlKey: true}, win); - is(iframeDoc.body.textContent, "", "deleted the text"); + is(iframeDoc.body.textContent.trim(), "", "deleted the text"); win.close(); SimpleTest.finish(); }, win); diff --git a/editor/libeditor/tests/test_bug966552.html b/editor/libeditor/tests/test_bug966552.html index 3d0ec5fe3..e18883aad 100644 --- a/editor/libeditor/tests/test_bug966552.html +++ b/editor/libeditor/tests/test_bug966552.html @@ -18,13 +18,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=966552 <script class="testbody" type="text/javascript"> SimpleTest.waitForExplicitFinish(); -addLoadEvent(function() { - var win = window.open("data:text/html,<body onload=\"document.designMode='on'\">test</body>", "", "test-966552"); - win.addEventListener("load", function onLoad() { - win.removeEventListener("load", onLoad); - runTest(win); - }, false); -}); +var win = window.open("file_bug966552.html", "", "test-966552"); +win.addEventListener("load", function() { + runTest(win); +}, {once: true}); function runTest(win) { SimpleTest.waitForFocus(function() { @@ -33,7 +30,7 @@ function runTest(win) { doc.body.focus(); sel.collapse(doc.body.firstChild, 2); synthesizeKey("VK_BACK_SPACE", {ctrlKey: true}, win); - is(doc.body.textContent, "st"); + is(doc.body.textContent.trim(), "st"); win.close(); SimpleTest.finish(); }, win); diff --git a/editor/libeditor/tests/test_pasteImgTextarea.html b/editor/libeditor/tests/test_pasteImgTextarea.html new file mode 100644 index 000000000..3168ae729 --- /dev/null +++ b/editor/libeditor/tests/test_pasteImgTextarea.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/SpawnTask.js"></script> +<img id="i" src="green.png"> +<textarea id="t"></textarea> + +<script> +let loaded = new Promise(resolve => addLoadEvent(resolve)); + add_task(function*() { + yield loaded; + SpecialPowers.setCommandNode(window, document.getElementById("i")); + SpecialPowers.doCommand(window, "cmd_copyImageContents"); + let input = document.getElementById("t"); + input.focus(); + var controller = + SpecialPowers.wrap(input).controllers.getControllerForCommand("cmd_paste"); + is(controller.isCommandEnabled("cmd_paste"), true, + "paste should be enabled in html textareas when an image is on the clipboard"); + }); +</script> diff --git a/editor/libeditor/tests/test_pasteImgTextarea.xul b/editor/libeditor/tests/test_pasteImgTextarea.xul new file mode 100644 index 000000000..545027aa3 --- /dev/null +++ b/editor/libeditor/tests/test_pasteImgTextarea.xul @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<window xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <html:img id="i" src="green.png" /> + <html:textarea id="t"></html:textarea> + </body> + <script type="text/javascript"><![CDATA[ + let loaded = new Promise(resolve => addLoadEvent(resolve)); + add_task(function*() { + yield loaded; + SpecialPowers.setCommandNode(window, document.getElementById("i")); + SpecialPowers.doCommand(window, "cmd_copyImageContents"); + let input = document.getElementById("t"); + input.focus(); + var controller = + SpecialPowers.wrap(input).controllers.getControllerForCommand("cmd_paste"); + is(controller.isCommandEnabled("cmd_paste"), false, + "paste should not be enabled in xul textareas when an image is on the clipboard"); + }); + ]]></script> +</window> |