Bug 591047 - (5/7) Adding IME notification support to content fake widget; r=roc a=blocking-fennec

This commit is contained in:
Brad Lassey 2010-09-23 23:28:15 -04:00
parent bc89960daa
commit ae74ab03d0
6 changed files with 392 additions and 0 deletions

View File

@ -58,6 +58,7 @@ using nsTextEvent;
using nsQueryContentEvent;
using nsSelectionEvent;
using RemoteDOMEvent;
using nsIMEUpdatePreference;
namespace mozilla {
namespace dom {
@ -90,6 +91,69 @@ parent:
sync SyncMessage(nsString aMessage, nsString aJSON)
returns (nsString[] retval);
/**
* Notifies chrome that there is a focus change involving an editable
* object (input, textarea, document, contentEditable. etc.)
*
* focus PR_TRUE if editable object is receiving focus
* PR_FALSE if losing focus
* preference Native widget preference for IME updates
*/
sync NotifyIMEFocus(PRBool focus)
returns (nsIMEUpdatePreference preference);
/**
* Notifies chrome that there has been a change in text content
* One call can encompass both a delete and an insert operation
* Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
*
* offset Starting offset of the change
* end Ending offset of the range deleted
* newEnd New ending offset after insertion
*
* for insertion, offset == end
* for deletion, offset == newEnd
*/
NotifyIMETextChange(PRUint32 offset, PRUint32 end, PRUint32 newEnd);
/**
* Notifies chrome that there has been a change in selection
* Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
*
* anchor Offset where the selection started
* focus Offset where the caret is
*/
NotifyIMESelection(PRUint32 anchor, PRUint32 focus);
/**
* Notifies chrome to refresh its text cache
* Only called when NotifyIMEFocus returns PR_TRUE for mWantHints
*
* text The entire content of the text field
*/
NotifyIMETextHint(nsString text);
/**
* Instructs chrome to end any pending composition
*
* cancel PR_TRUE if composition should be cancelled
* composition Text to commit before ending the composition
*
* if cancel is PR_TRUE,
* widget should return empty string for composition
* if cancel is PR_FALSE,
* widget should return the current composition text
*/
sync EndIMEComposition(PRBool cancel) returns (nsString composition);
sync GetIMEEnabled() returns (PRUint32 value);
SetIMEEnabled(PRUint32 value);
sync GetIMEOpenState() returns (PRBool value);
SetIMEOpenState(PRBool value);
PContentPermissionRequest(nsCString aType, URI uri);
PContentDialog(PRUint32 aType, nsCString aName, nsCString aFeatures,

View File

@ -311,6 +311,114 @@ TabParent::RecvAsyncMessage(const nsString& aMessage,
return ReceiveMessage(aMessage, PR_FALSE, aJSON, nsnull);
}
bool
TabParent::RecvNotifyIMEFocus(const PRBool& aFocus,
nsIMEUpdatePreference* aPreference)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return true;
nsresult rv = widget->OnIMEFocusChange(aFocus);
if (aFocus) {
if (NS_SUCCEEDED(rv) && rv != NS_SUCCESS_IME_NO_UPDATES) {
*aPreference = widget->GetIMEUpdatePreference();
} else {
aPreference->mWantUpdates = PR_FALSE;
aPreference->mWantHints = PR_FALSE;
}
} else {
mIMECacheText.Truncate(0);
}
return true;
}
bool
TabParent::RecvNotifyIMETextChange(const PRUint32& aStart,
const PRUint32& aEnd,
const PRUint32& aNewEnd)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return true;
widget->OnIMETextChange(aStart, aEnd, aNewEnd);
return true;
}
bool
TabParent::RecvNotifyIMESelection(const PRUint32& aAnchor,
const PRUint32& aFocus)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return true;
widget->OnIMESelectionChange();
return true;
}
bool
TabParent::RecvNotifyIMETextHint(const nsString& aText)
{
// Replace our cache with new text
mIMECacheText = aText;
return true;
}
bool
TabParent::RecvEndIMEComposition(const PRBool& aCancel,
nsString* aComposition)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return true;
if (aCancel) {
widget->CancelIMEComposition();
} else {
widget->ResetInputState();
}
return true;
}
bool
TabParent::RecvGetIMEEnabled(PRUint32* aValue)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (widget)
widget->GetIMEEnabled(aValue);
return true;
}
bool
TabParent::RecvSetIMEEnabled(const PRUint32& aValue)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (widget)
widget->SetIMEEnabled(aValue);
return true;
}
bool
TabParent::RecvGetIMEOpenState(PRBool* aValue)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (widget)
widget->GetIMEOpenState(aValue);
return true;
}
bool
TabParent::RecvSetIMEOpenState(const PRBool& aValue)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (widget)
widget->SetIMEOpenState(aValue);
return true;
}
bool
TabParent::ReceiveMessage(const nsString& aMessage,
PRBool aSync,
@ -469,5 +577,19 @@ TabParent::GetFrameLoader() const
return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nsnull;
}
already_AddRefed<nsIWidget>
TabParent::GetWidget() const
{
nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
if (!content)
return nsnull;
nsIFrame *frame = content->GetPrimaryFrame();
if (!frame)
return nsnull;
return nsCOMPtr<nsIWidget>(frame->GetNearestWidget()).forget();
}
} // namespace tabs
} // namespace mozilla

View File

@ -91,6 +91,20 @@ public:
nsTArray<nsString>* aJSONRetVal);
virtual bool RecvAsyncMessage(const nsString& aMessage,
const nsString& aJSON);
virtual bool RecvNotifyIMEFocus(const PRBool& aFocus,
nsIMEUpdatePreference* aPreference);
virtual bool RecvNotifyIMETextChange(const PRUint32& aStart,
const PRUint32& aEnd,
const PRUint32& aNewEnd);
virtual bool RecvNotifyIMESelection(const PRUint32& aAnchor,
const PRUint32& aFocus);
virtual bool RecvNotifyIMETextHint(const nsString& aText);
virtual bool RecvEndIMEComposition(const PRBool& aCancel,
nsString* aComposition);
virtual bool RecvGetIMEEnabled(PRUint32* aValue);
virtual bool RecvSetIMEEnabled(const PRUint32& aValue);
virtual bool RecvGetIMEOpenState(PRBool* aValue);
virtual bool RecvSetIMEOpenState(const PRBool& aValue);
virtual PContentDialogParent* AllocPContentDialog(const PRUint32& aType,
const nsCString& aName,
const nsCString& aFeatures,
@ -201,8 +215,12 @@ protected:
nsString mSecurityTooltipText;
nsCOMPtr<nsISupports> mSecurityStatusObject;
// IME
nsString mIMECacheText;
private:
already_AddRefed<nsFrameLoader> GetFrameLoader() const;
already_AddRefed<nsIWidget> GetWidget() const;
};
} // namespace dom

View File

@ -279,6 +279,24 @@ struct ParamTraits<nsSelectionEvent>
}
};
template<>
struct ParamTraits<nsIMEUpdatePreference>
{
typedef nsIMEUpdatePreference paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mWantUpdates);
WriteParam(aMsg, aParam.mWantHints);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
return ReadParam(aMsg, aIter, &aResult->mWantUpdates) &&
ReadParam(aMsg, aIter, &aResult->mWantHints);
}
};
} // namespace IPC
#endif // nsGUIEventIPC_h__

View File

@ -110,6 +110,8 @@ PuppetWidget::Create(nsIWidget *aParent,
->CreateOffscreenSurface(gfxIntSize(1, 1),
gfxASurface::ContentFromFormat(gfxASurface::ImageFormatARGB32));
mIMEComposing = PR_FALSE;
PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
if (parent) {
parent->SetChild(this);
@ -272,7 +274,14 @@ PuppetWidget::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
aStatus = nsEventStatus_eIgnore;
if (mEventCallback) {
if (event->message == NS_COMPOSITION_START) {
mIMEComposing = PR_TRUE;
}
aStatus = (*mEventCallback)(event);
if (event->message == NS_COMPOSITION_END) {
mIMEComposing = PR_FALSE;
}
} else if (mChild) {
event->widget = mChild;
mChild->DispatchEvent(event, aStatus);
@ -296,6 +305,151 @@ PuppetWidget::GetThebesSurface()
return mSurface;
}
nsresult
PuppetWidget::IMEEndComposition(PRBool aCancel)
{
if (!mIMEComposing)
return NS_OK;
nsEventStatus status;
nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, this);
InitEvent(textEvent, nsnull);
if (!mTabChild ||
!mTabChild->SendEndIMEComposition(aCancel, &textEvent.theText)) {
return NS_ERROR_FAILURE;
}
DispatchEvent(&textEvent, status);
nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_END, this);
InitEvent(compEvent, nsnull);
DispatchEvent(&compEvent, status);
return NS_OK;
}
NS_IMETHODIMP
PuppetWidget::ResetInputState()
{
return IMEEndComposition(PR_FALSE);
}
NS_IMETHODIMP
PuppetWidget::CancelComposition()
{
return IMEEndComposition(PR_TRUE);
}
NS_IMETHODIMP
PuppetWidget::SetIMEOpenState(PRBool aState)
{
if (mTabChild &&
mTabChild->SendSetIMEOpenState(aState))
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PuppetWidget::SetIMEEnabled(PRUint32 aState)
{
if (mTabChild &&
mTabChild->SendSetIMEEnabled(aState))
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PuppetWidget::GetIMEOpenState(PRBool *aState)
{
if (mTabChild &&
mTabChild->SendGetIMEOpenState(aState))
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PuppetWidget::GetIMEEnabled(PRUint32 *aState)
{
if (mTabChild &&
mTabChild->SendGetIMEEnabled(aState))
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PuppetWidget::OnIMEFocusChange(PRBool aFocus)
{
if (!mTabChild)
return NS_ERROR_FAILURE;
if (aFocus) {
nsEventStatus status;
nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_TEXT_CONTENT, this);
InitEvent(queryEvent, nsnull);
// Query entire content
queryEvent.InitForQueryTextContent(0, PR_UINT32_MAX);
DispatchEvent(&queryEvent, status);
if (queryEvent.mSucceeded) {
mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
}
}
mIMEPreference.mWantUpdates = PR_FALSE;
mIMEPreference.mWantHints = PR_FALSE;
if (!mTabChild->SendNotifyIMEFocus(aFocus, &mIMEPreference))
return NS_ERROR_FAILURE;
if (aFocus) {
if (!mIMEPreference.mWantUpdates && !mIMEPreference.mWantHints)
// call OnIMEFocusChange on blur but no other updates
return NS_SUCCESS_IME_NO_UPDATES;
OnIMESelectionChange(); // Update selection
}
return NS_OK;
}
NS_IMETHODIMP
PuppetWidget::OnIMETextChange(PRUint32 aStart, PRUint32 aEnd, PRUint32 aNewEnd)
{
if (!mTabChild)
return NS_ERROR_FAILURE;
if (mIMEPreference.mWantHints) {
nsEventStatus status;
nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_TEXT_CONTENT, this);
InitEvent(queryEvent, nsnull);
queryEvent.InitForQueryTextContent(0, PR_UINT32_MAX);
DispatchEvent(&queryEvent, status);
if (queryEvent.mSucceeded) {
mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
}
}
if (mIMEPreference.mWantUpdates) {
mTabChild->SendNotifyIMETextChange(aStart, aEnd, aNewEnd);
}
return NS_OK;
}
NS_IMETHODIMP
PuppetWidget::OnIMESelectionChange(void)
{
if (!mTabChild)
return NS_ERROR_FAILURE;
if (mIMEPreference.mWantUpdates) {
nsEventStatus status;
nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_SELECTED_TEXT, this);
InitEvent(queryEvent, nsnull);
DispatchEvent(&queryEvent, status);
if (queryEvent.mSucceeded) {
mTabChild->SendNotifyIMESelection(queryEvent.GetSelectionStart(),
queryEvent.GetSelectionEnd());
}
}
return NS_OK;
}
nsresult
PuppetWidget::DispatchPaintEvent()
{

View File

@ -168,12 +168,25 @@ public:
// virtual nsIDeviceContext* GetDeviceContext();
virtual gfxASurface* GetThebesSurface();
NS_IMETHOD ResetInputState();
NS_IMETHOD SetIMEOpenState(PRBool aState);
NS_IMETHOD GetIMEOpenState(PRBool *aState);
NS_IMETHOD SetIMEEnabled(PRUint32 aState);
NS_IMETHOD GetIMEEnabled(PRUint32 *aState);
NS_IMETHOD CancelComposition();
NS_IMETHOD OnIMEFocusChange(PRBool aFocus);
NS_IMETHOD OnIMETextChange(PRUint32 aOffset, PRUint32 aEnd,
PRUint32 aNewEnd);
NS_IMETHOD OnIMESelectionChange(void);
private:
nsresult DispatchPaintEvent();
nsresult DispatchResizeEvent();
void SetChild(PuppetWidget* aChild);
nsresult IMEEndComposition(PRBool aCancel);
class PaintTask : public nsRunnable {
public:
NS_DECL_NSIRUNNABLE
@ -200,6 +213,9 @@ private:
// XXX/cjones: keeping this around until we teach LayerManager to do
// retained-content-only transactions
nsRefPtr<gfxASurface> mSurface;
// IME
nsIMEUpdatePreference mIMEPreference;
PRPackedBool mIMEComposing;
};
} // namespace widget