mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 05:48:26 +00:00
Bug 705057 part.1 Ensure a set of composition events is fired on same content r=smaug
This commit is contained in:
parent
d3f82bacd5
commit
8acd6ae7f5
@ -61,6 +61,7 @@ CPPSRCS = \
|
||||
nsDOMTouchEvent.cpp \
|
||||
nsDOMCompositionEvent.cpp \
|
||||
DOMWheelEvent.cpp \
|
||||
TextComposition.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_B2G_RIL
|
||||
|
136
content/events/src/TextComposition.cpp
Normal file
136
content/events/src/TextComposition.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TextComposition.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/******************************************************************************
|
||||
* TextComposition
|
||||
******************************************************************************/
|
||||
|
||||
TextComposition::TextComposition(nsPresContext* aPresContext,
|
||||
nsINode* aNode,
|
||||
nsGUIEvent* aEvent) :
|
||||
mPresContext(aPresContext), mNode(aNode),
|
||||
// temporarily, we should assume that one native IME context is per native
|
||||
// widget.
|
||||
mNativeContext(aEvent->widget)
|
||||
{
|
||||
}
|
||||
|
||||
TextComposition::TextComposition(const TextComposition& aOther)
|
||||
{
|
||||
mNativeContext = aOther.mNativeContext;
|
||||
mPresContext = aOther.mPresContext;
|
||||
mNode = aOther.mNode;
|
||||
}
|
||||
|
||||
bool
|
||||
TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
|
||||
{
|
||||
// temporarily, we should assume that one native IME context is per one
|
||||
// native widget.
|
||||
return mNativeContext == static_cast<void*>(aWidget);
|
||||
}
|
||||
|
||||
bool
|
||||
TextComposition::MatchesEventTarget(nsPresContext* aPresContext,
|
||||
nsINode* aNode) const
|
||||
{
|
||||
return mPresContext == aPresContext && mNode == aNode;
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::DispatchEvent(nsGUIEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
nsDispatchingCallback* aCallBack)
|
||||
{
|
||||
nsEventDispatcher::Dispatch(mNode, mPresContext,
|
||||
aEvent, nullptr, aStatus, aCallBack);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* TextCompositionArray
|
||||
******************************************************************************/
|
||||
|
||||
TextCompositionArray::index_type
|
||||
TextCompositionArray::IndexOf(nsIWidget* aWidget)
|
||||
{
|
||||
for (index_type i = Length(); i > 0; --i) {
|
||||
if (ElementAt(i - 1).MatchesNativeContext(aWidget)) {
|
||||
return i - 1;
|
||||
}
|
||||
}
|
||||
return NoIndex;
|
||||
}
|
||||
|
||||
TextCompositionArray::index_type
|
||||
TextCompositionArray::IndexOf(nsPresContext* aPresContext)
|
||||
{
|
||||
for (index_type i = Length(); i > 0; --i) {
|
||||
if (ElementAt(i - 1).GetPresContext() == aPresContext) {
|
||||
return i - 1;
|
||||
}
|
||||
}
|
||||
return NoIndex;
|
||||
}
|
||||
|
||||
TextCompositionArray::index_type
|
||||
TextCompositionArray::IndexOf(nsPresContext* aPresContext,
|
||||
nsINode* aNode)
|
||||
{
|
||||
index_type index = IndexOf(aPresContext);
|
||||
if (index == NoIndex) {
|
||||
return NoIndex;
|
||||
}
|
||||
nsINode* node = ElementAt(index).GetEventTargetNode();
|
||||
return node == aNode ? index : NoIndex;
|
||||
}
|
||||
|
||||
TextComposition*
|
||||
TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
|
||||
{
|
||||
index_type i = IndexOf(aWidget);
|
||||
return i != NoIndex ? &ElementAt(i) : nullptr;
|
||||
}
|
||||
|
||||
TextComposition*
|
||||
TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext)
|
||||
{
|
||||
index_type i = IndexOf(aPresContext);
|
||||
return i != NoIndex ? &ElementAt(i) : nullptr;
|
||||
}
|
||||
|
||||
TextComposition*
|
||||
TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
|
||||
nsINode* aNode)
|
||||
{
|
||||
index_type i = IndexOf(aPresContext, aNode);
|
||||
return i != NoIndex ? &ElementAt(i) : nullptr;
|
||||
}
|
||||
|
||||
TextComposition*
|
||||
TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
{
|
||||
// There should be only one composition per content object.
|
||||
for (index_type i = Length(); i > 0; --i) {
|
||||
nsINode* node = ElementAt(i - 1).GetEventTargetNode();
|
||||
if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
|
||||
return &ElementAt(i - 1);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
103
content/events/src/TextComposition.h
Normal file
103
content/events/src/TextComposition.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_TextComposition_h
|
||||
#define mozilla_TextComposition_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsEvent.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsCompositionEvent;
|
||||
class nsDispatchingCallback;
|
||||
class nsIMEStateManager;
|
||||
class nsIWidget;
|
||||
class nsPresContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* TextComposition represents a text composition. This class stores the
|
||||
* composition event target and its presContext. At dispatching the event via
|
||||
* this class, the instances use the stored event target.
|
||||
*/
|
||||
|
||||
class TextComposition MOZ_FINAL
|
||||
{
|
||||
friend class ::nsIMEStateManager;
|
||||
public:
|
||||
TextComposition(nsPresContext* aPresContext,
|
||||
nsINode* aNode,
|
||||
nsGUIEvent* aEvent);
|
||||
|
||||
TextComposition(const TextComposition& aOther);
|
||||
|
||||
~TextComposition()
|
||||
{
|
||||
// WARNING: mPresContext may be destroying, so, be careful if you touch it.
|
||||
}
|
||||
|
||||
nsPresContext* GetPresContext() const { return mPresContext; }
|
||||
nsINode* GetEventTargetNode() const { return mNode; }
|
||||
|
||||
bool MatchesNativeContext(nsIWidget* aWidget) const;
|
||||
bool MatchesEventTarget(nsPresContext* aPresContext,
|
||||
nsINode* aNode) const;
|
||||
|
||||
private:
|
||||
// This class holds nsPresContext weak. This instance shouldn't block
|
||||
// destroying it. When the presContext is being destroyed, it's notified to
|
||||
// nsIMEStateManager::OnDestroyPresContext(), and then, it destroy
|
||||
// this instance.
|
||||
nsPresContext* mPresContext;
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
|
||||
// mNativeContext stores a opaque pointer. This works as the "ID" for this
|
||||
// composition. Don't access the instance, it may not be available.
|
||||
void* mNativeContext;
|
||||
|
||||
// Hide the default constructor
|
||||
TextComposition() {}
|
||||
|
||||
/**
|
||||
* DispatchEvent() dispatches the aEvent to the mContent synchronously.
|
||||
* The caller must ensure that it's safe to dispatch the event.
|
||||
*/
|
||||
void DispatchEvent(nsGUIEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
nsDispatchingCallback* aCallBack);
|
||||
};
|
||||
|
||||
/**
|
||||
* TextCompositionArray manages the instances of TextComposition class.
|
||||
* Managing with array is enough because only one composition is typically
|
||||
* there. Even if user switches native IME context, it's very rare that
|
||||
* second or more composition is started.
|
||||
* It's assumed that this is used by nsIMEStateManager for storing all active
|
||||
* compositions in the process. If the instance is it, each TextComposition
|
||||
* in the array can be destroyed by calling some methods of itself.
|
||||
*/
|
||||
|
||||
class TextCompositionArray MOZ_FINAL : public nsAutoTArray<TextComposition, 2>
|
||||
{
|
||||
public:
|
||||
index_type IndexOf(nsIWidget* aWidget);
|
||||
index_type IndexOf(nsPresContext* aPresContext);
|
||||
index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode);
|
||||
|
||||
TextComposition* GetCompositionFor(nsIWidget* aWidget);
|
||||
TextComposition* GetCompositionFor(nsPresContext* aPresContext);
|
||||
TextComposition* GetCompositionFor(nsPresContext* aPresContext,
|
||||
nsINode* aNode);
|
||||
TextComposition* GetCompositionInContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef mozilla_TextComposition_h
|
@ -4886,6 +4886,8 @@ nsEventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent
|
||||
element->LeaveLink(element->GetPresContext());
|
||||
}
|
||||
|
||||
nsIMEStateManager::OnRemoveContent(mPresContext, aContent);
|
||||
|
||||
// inform the focus manager that the content is being removed. If this
|
||||
// content is focused, the focus will be removed without firing events.
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
|
@ -34,7 +34,10 @@
|
||||
#include "nsIForm.h"
|
||||
#include "nsHTMLFormElement.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "TextComposition.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::widget;
|
||||
|
||||
/******************************************************************/
|
||||
@ -47,11 +50,33 @@ bool nsIMEStateManager::sInstalledMenuKeyboardListener = false;
|
||||
bool nsIMEStateManager::sInSecureInputMode = false;
|
||||
|
||||
nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
|
||||
TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr;
|
||||
|
||||
void
|
||||
nsIMEStateManager::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
|
||||
delete sTextCompositions;
|
||||
sTextCompositions = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPresContext);
|
||||
|
||||
// First, if there is a composition in the aPresContext, clean up it.
|
||||
if (sTextCompositions) {
|
||||
TextCompositionArray::index_type i =
|
||||
sTextCompositions->IndexOf(aPresContext);
|
||||
if (i != TextCompositionArray::NoIndex) {
|
||||
// there should be only one composition per presContext object.
|
||||
sTextCompositions->RemoveElementAt(i);
|
||||
MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) ==
|
||||
TextCompositionArray::NoIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (aPresContext != sPresContext)
|
||||
return NS_OK;
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
|
||||
@ -72,17 +97,35 @@ nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPresContext);
|
||||
|
||||
// First, if there is a composition in the aContent, clean up it.
|
||||
if (sTextCompositions) {
|
||||
TextComposition* compositionInContent =
|
||||
sTextCompositions->GetCompositionInContent(aPresContext, aContent);
|
||||
if (compositionInContent) {
|
||||
// Try resetting the native IME state. Be aware, typically, this method
|
||||
// is called during the content being removed. Then, the native
|
||||
// composition events which are caused by following APIs are ignored due
|
||||
// to unsafe to run script (in PresShell::HandleEvent()).
|
||||
nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
|
||||
if (widget) {
|
||||
nsresult rv = widget->CancelIMEComposition();
|
||||
if (NS_FAILED(rv)) {
|
||||
widget->ResetInputState();
|
||||
}
|
||||
// WARNING: the |compositionInContent| may have been destroyed.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sPresContext || !sContent ||
|
||||
aPresContext != sPresContext ||
|
||||
aContent != sContent)
|
||||
!nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Current IME transaction should commit
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
|
||||
if (widget) {
|
||||
nsresult rv = widget->CancelIMEComposition();
|
||||
if (NS_FAILED(rv))
|
||||
widget->ResetInputState();
|
||||
IMEState newState = GetNewIMEState(sPresContext, nullptr);
|
||||
InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
|
||||
InputContextAction::LOST_FOCUS);
|
||||
@ -384,6 +427,61 @@ nsIMEStateManager::GetWidget(nsPresContext* aPresContext)
|
||||
return widget;
|
||||
}
|
||||
|
||||
void
|
||||
nsIMEStateManager::EnsureTextCompositionArray()
|
||||
{
|
||||
if (sTextCompositions) {
|
||||
return;
|
||||
}
|
||||
sTextCompositions = new TextCompositionArray();
|
||||
}
|
||||
|
||||
void
|
||||
nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
|
||||
nsPresContext* aPresContext,
|
||||
nsEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
nsDispatchingCallback* aCallBack)
|
||||
{
|
||||
MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
|
||||
aEvent->eventStructType == NS_TEXT_EVENT);
|
||||
if (!NS_IS_TRUSTED_EVENT(aEvent) ||
|
||||
(aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureTextCompositionArray();
|
||||
|
||||
nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);
|
||||
|
||||
TextComposition* composition =
|
||||
sTextCompositions->GetCompositionFor(GUIEvent->widget);
|
||||
if (!composition) {
|
||||
MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
|
||||
TextComposition newComposition(aPresContext, aEventTargetNode, GUIEvent);
|
||||
composition = sTextCompositions->AppendElement(newComposition);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else {
|
||||
MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
|
||||
}
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
// Dispatch the event on composing target.
|
||||
composition->DispatchEvent(GUIEvent, aStatus, aCallBack);
|
||||
|
||||
// WARNING: the |composition| might have been destroyed already.
|
||||
|
||||
// Remove the ended composition from the array.
|
||||
if (aEvent->message == NS_COMPOSITION_END) {
|
||||
TextCompositionArray::index_type i =
|
||||
sTextCompositions->IndexOf(GUIEvent->widget);
|
||||
if (i != TextCompositionArray::NoIndex) {
|
||||
sTextCompositions->RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// nsTextStateManager notifies widget of any text and selection changes
|
||||
// in the currently focused editor
|
||||
|
@ -9,13 +9,19 @@
|
||||
#include "nscore.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
class nsDispatchingCallback;
|
||||
class nsIContent;
|
||||
class nsIDOMMouseEvent;
|
||||
class nsINode;
|
||||
class nsPIDOMWindow;
|
||||
class nsPresContext;
|
||||
class nsTextStateManager;
|
||||
class nsISelection;
|
||||
|
||||
namespace mozilla {
|
||||
class TextCompositionArray;
|
||||
} // namespace mozilla
|
||||
|
||||
/*
|
||||
* IME state manager
|
||||
*/
|
||||
@ -28,6 +34,8 @@ protected:
|
||||
typedef mozilla::widget::InputContextAction InputContextAction;
|
||||
|
||||
public:
|
||||
static void Shutdown();
|
||||
|
||||
static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
|
||||
static nsresult OnRemoveContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent);
|
||||
@ -75,6 +83,19 @@ public:
|
||||
nsIContent* aContent,
|
||||
nsIDOMMouseEvent* aMouseEvent);
|
||||
|
||||
/**
|
||||
* All DOM composition events and DOM text events must be dispatched via
|
||||
* DispatchCompositionEvent() for storing the composition target
|
||||
* and ensuring a set of composition events must be fired the stored target.
|
||||
* If the stored composition event target is destroying, this removes the
|
||||
* stored composition automatically.
|
||||
*/
|
||||
static void DispatchCompositionEvent(nsINode* aEventTargetNode,
|
||||
nsPresContext* aPresContext,
|
||||
nsEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
nsDispatchingCallback* aCallBack);
|
||||
|
||||
protected:
|
||||
static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
|
||||
nsIContent* aContent,
|
||||
@ -87,6 +108,7 @@ protected:
|
||||
nsIContent* aContent);
|
||||
|
||||
static nsIWidget* GetWidget(nsPresContext* aPresContext);
|
||||
static void EnsureTextCompositionArray();
|
||||
|
||||
static nsIContent* sContent;
|
||||
static nsPresContext* sPresContext;
|
||||
@ -94,6 +116,12 @@ protected:
|
||||
static bool sInSecureInputMode;
|
||||
|
||||
static nsTextStateManager* sTextStateObserver;
|
||||
|
||||
// All active compositions in the process are stored by this array.
|
||||
// When you get an item of this array and use it, please be careful.
|
||||
// The instances in this array can be destroyed automatically if you do
|
||||
// something to cause committing or canceling the composition.
|
||||
static mozilla::TextCompositionArray* sTextCompositions;
|
||||
};
|
||||
|
||||
#endif // nsIMEStateManager_h__
|
||||
|
@ -812,13 +812,6 @@ nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
|
||||
bool shouldShowFocusRing = window->ShouldShowFocusRing();
|
||||
window->SetFocusedNode(nullptr);
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsIPresShell> presShell;
|
||||
docShell->GetPresShell(getter_AddRefs(presShell));
|
||||
nsIMEStateManager::OnRemoveContent(presShell->GetPresContext(), content);
|
||||
}
|
||||
|
||||
// if this window is currently focused, clear the global focused
|
||||
// element as well, but don't fire any events.
|
||||
if (window == mFocusedWindow) {
|
||||
|
@ -1170,6 +1170,15 @@ nsPresContext::GetToplevelContentDocumentPresContext()
|
||||
}
|
||||
}
|
||||
|
||||
nsIWidget*
|
||||
nsPresContext::GetNearestWidget(nsPoint* aOffset)
|
||||
{
|
||||
NS_ENSURE_TRUE(mShell, nullptr);
|
||||
nsIFrame* frame = mShell->GetRootFrame();
|
||||
NS_ENSURE_TRUE(frame, nullptr);
|
||||
return frame->GetView()->GetNearestWidget(aOffset);
|
||||
}
|
||||
|
||||
// We may want to replace this with something faster, maybe caching the root prescontext
|
||||
nsRootPresContext*
|
||||
nsPresContext::GetRootPresContext()
|
||||
|
@ -190,6 +190,16 @@ public:
|
||||
*/
|
||||
nsPresContext* GetToplevelContentDocumentPresContext();
|
||||
|
||||
/**
|
||||
* Returns the nearest widget for the root frame of this.
|
||||
*
|
||||
* @param aOffset If non-null the offset from the origin of the root
|
||||
* frame's view to the widget's origin (usually positive)
|
||||
* expressed in appunits of this will be returned in
|
||||
* aOffset.
|
||||
*/
|
||||
nsIWidget* GetNearestWidget(nsPoint* aOffset = nullptr);
|
||||
|
||||
/**
|
||||
* Return the presentation context for the root of the view manager
|
||||
* hierarchy that contains this presentation context, or nullptr if it can't
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "nsViewsCID.h"
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsEventStateManager.h"
|
||||
#include "nsIMEStateManager.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsILayoutHistoryState.h"
|
||||
@ -6435,23 +6436,33 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus)
|
||||
nsPresShellEventCB eventCB(this);
|
||||
if (aEvent->eventStructType == NS_TOUCH_EVENT) {
|
||||
DispatchTouchEvent(aEvent, aStatus, &eventCB, touchIsNew);
|
||||
}
|
||||
else if (mCurrentEventContent) {
|
||||
nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext,
|
||||
aEvent, nullptr, aStatus, &eventCB);
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIContent> targetContent;
|
||||
if (mCurrentEventFrame) {
|
||||
rv = mCurrentEventFrame->GetContentForEvent(aEvent,
|
||||
getter_AddRefs(targetContent));
|
||||
} else {
|
||||
nsCOMPtr<nsINode> eventTarget = mCurrentEventContent.get();
|
||||
nsPresShellEventCB* eventCBPtr = &eventCB;
|
||||
if (!eventTarget) {
|
||||
nsCOMPtr<nsIContent> targetContent;
|
||||
if (mCurrentEventFrame) {
|
||||
rv = mCurrentEventFrame->
|
||||
GetContentForEvent(aEvent, getter_AddRefs(targetContent));
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) && targetContent) {
|
||||
eventTarget = do_QueryInterface(targetContent);
|
||||
} else if (mDocument) {
|
||||
eventTarget = do_QueryInterface(mDocument);
|
||||
// If we don't have any content, the callback wouldn't probably
|
||||
// do nothing.
|
||||
eventCBPtr = nullptr;
|
||||
}
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) && targetContent) {
|
||||
nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent,
|
||||
nullptr, aStatus, &eventCB);
|
||||
} else if (mDocument) {
|
||||
nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent,
|
||||
nullptr, aStatus, nullptr);
|
||||
if (eventTarget) {
|
||||
if (aEvent->eventStructType == NS_COMPOSITION_EVENT ||
|
||||
aEvent->eventStructType == NS_TEXT_EVENT) {
|
||||
nsIMEStateManager::DispatchCompositionEvent(eventTarget,
|
||||
mPresContext, aEvent, aStatus, eventCBPtr);
|
||||
} else {
|
||||
nsEventDispatcher::Dispatch(eventTarget, mPresContext,
|
||||
aEvent, nullptr, aStatus, eventCBPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,18 +13,7 @@
|
||||
iframe.focus();
|
||||
doc.body.focus();
|
||||
win.getSelection().collapse(doc.body, 0);
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{
|
||||
"string": "Here's some text.",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 17, "attr": COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
}
|
||||
)
|
||||
sendString("Here's some text.");
|
||||
synthesizeKey("VK_RETURN", {});
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
@ -103,6 +103,7 @@
|
||||
#include "nsCookieService.h"
|
||||
#include "nsApplicationCacheService.h"
|
||||
#include "mozilla/dom/time/DateCacheCleaner.h"
|
||||
#include "nsIMEStateManager.h"
|
||||
|
||||
extern void NS_ShutdownChainItemPool();
|
||||
|
||||
@ -284,6 +285,7 @@ nsLayoutStatics::Shutdown()
|
||||
txMozillaXSLTProcessor::Shutdown();
|
||||
nsDOMAttribute::Shutdown();
|
||||
nsEventListenerManager::Shutdown();
|
||||
nsIMEStateManager::Shutdown();
|
||||
nsComputedDOMStyle::Shutdown();
|
||||
nsCSSParser::Shutdown();
|
||||
nsCSSRuleProcessor::Shutdown();
|
||||
|
@ -86,20 +86,47 @@ function sendMouseEvent(aEvent, aTarget, aWindow) {
|
||||
* chars (sends the right charcode, and sends a shift key for uppercase chars).
|
||||
* No other modifiers are handled at this point.
|
||||
*
|
||||
* For now this method only works for English letters (lower and upper case)
|
||||
* and the digits 0-9.
|
||||
* For now this method only works for ASCII characters and emulates the shift
|
||||
* key state on US keyboard layout.
|
||||
*/
|
||||
function sendChar(aChar, aWindow) {
|
||||
// DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9.
|
||||
var hasShift = (aChar == aChar.toUpperCase());
|
||||
var hasShift;
|
||||
// Emulate US keyboard layout for the shiftKey state.
|
||||
switch (aChar) {
|
||||
case "!":
|
||||
case "@":
|
||||
case "#":
|
||||
case "$":
|
||||
case "%":
|
||||
case "^":
|
||||
case "&":
|
||||
case "*":
|
||||
case "(":
|
||||
case ")":
|
||||
case "_":
|
||||
case "+":
|
||||
case "{":
|
||||
case "}":
|
||||
case ":":
|
||||
case "\"":
|
||||
case "|":
|
||||
case "<":
|
||||
case ">":
|
||||
case "?":
|
||||
hasShift = true;
|
||||
break;
|
||||
default:
|
||||
hasShift = (aChar == aChar.toUpperCase());
|
||||
break;
|
||||
}
|
||||
synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the string aStr to the focused element.
|
||||
*
|
||||
* For now this method only works for English letters (lower and upper case)
|
||||
* and the digits 0-9.
|
||||
* For now this method only works for ASCII characters and emulates the shift
|
||||
* key state on US keyboard layout.
|
||||
*/
|
||||
function sendString(aStr, aWindow) {
|
||||
for (var i = 0; i < aStr.length; ++i) {
|
||||
|
@ -115,6 +115,7 @@ function starttest() {
|
||||
$("testKeyEvent").value = "";
|
||||
|
||||
/* test synthesizeComposition */
|
||||
$("textBoxB").focus();
|
||||
check = false;
|
||||
window.addEventListener("compositionstart", function() { check = true; }, false);
|
||||
synthesizeComposition({ type: "compositionstart" });
|
||||
@ -122,30 +123,41 @@ function starttest() {
|
||||
|
||||
check = false;
|
||||
window.addEventListener("compositionupdate", function() { check = true; }, false);
|
||||
synthesizeComposition({ type: "compositionupdate" });
|
||||
synthesizeComposition({ type: "compositionupdate", data: "a" });
|
||||
is(check, true, 'synthesizeComposition() should dispatch compositionupdate');
|
||||
|
||||
|
||||
check = false;
|
||||
window.addEventListener("compositionend", function() { check = true; }, false);
|
||||
synthesizeComposition({ type: "compositionend" });
|
||||
is(check, true, 'synthesizeComposition() should dispatch compositionend');
|
||||
check = false;
|
||||
|
||||
$("textBoxB").focus();
|
||||
const nsIDOMWindowUtils = SpecialPowers.Ci.nsIDOMWindowUtils;
|
||||
window.addEventListener("text", function() { check = true; }, false);
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "a",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
}
|
||||
);
|
||||
is(check, false, "synthesizeText shouldn't start or end composition");
|
||||
is($("textBoxB").value, "a", "synthesizeText should send text");
|
||||
is(check, true, "synthesizeText should dispatch text event");
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "a",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 0, "attr": 0 }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
}
|
||||
);
|
||||
|
||||
check = false;
|
||||
window.addEventListener("compositionend", function() { check = true; }, false);
|
||||
synthesizeComposition({ type: "compositionend", data: "a" });
|
||||
is(check, true, 'synthesizeComposition() should dispatch compositionend');
|
||||
|
||||
var querySelectedText = synthesizeQuerySelectedText();
|
||||
ok(querySelectedText, "query selected text event result is null");
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); //get permission to check members of querySelectedText
|
||||
|
Loading…
x
Reference in New Issue
Block a user