Bug 911951 part.1 Redesign nsIDOMWindowUtils::SendTextEvent() with nsICompositionStringSynthesizer r=smaug, sr=roc

This commit is contained in:
Masayuki Nakano 2013-09-13 00:19:00 +09:00
parent 7c8f4c7a6a
commit 3ec81b980d
7 changed files with 264 additions and 104 deletions

View File

@ -0,0 +1,151 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "CompositionStringSynthesizer.h"
#include "nsContentUtils.h"
#include "nsIDocShell.h"
#include "nsIFrame.h"
#include "nsIPresShell.h"
#include "nsIWidget.h"
#include "nsPIDOMWindow.h"
#include "nsView.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS1(CompositionStringSynthesizer,
nsICompositionStringSynthesizer)
CompositionStringSynthesizer::CompositionStringSynthesizer(
nsPIDOMWindow* aWindow)
{
mWindow = do_GetWeakReference(aWindow);
ClearInternal();
}
CompositionStringSynthesizer::~CompositionStringSynthesizer()
{
}
void
CompositionStringSynthesizer::ClearInternal()
{
mString.Truncate();
mClauses.Clear();
mCaret.mRangeType = 0;
}
nsIWidget*
CompositionStringSynthesizer::GetWidget()
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
if (!window) {
return nullptr;
}
nsIDocShell *docShell = window->GetDocShell();
if (!docShell) {
return nullptr;
}
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
if (!presShell) {
return nullptr;
}
nsIFrame* frame = presShell->GetRootFrame();
if (!frame) {
return nullptr;
}
return frame->GetView()->GetNearestWidget(nullptr);
}
NS_IMETHODIMP
CompositionStringSynthesizer::SetString(const nsAString& aString)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
mString = aString;
return NS_OK;
}
NS_IMETHODIMP
CompositionStringSynthesizer::AppendClause(uint32_t aLength,
uint32_t aAttribute)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
switch (aAttribute) {
case ATTR_RAWINPUT:
case ATTR_SELECTEDRAWTEXT:
case ATTR_CONVERTEDTEXT:
case ATTR_SELECTEDCONVERTEDTEXT: {
nsTextRange textRange;
textRange.mStartOffset =
mClauses.IsEmpty() ? 0 : mClauses[mClauses.Length() - 1].mEndOffset;
textRange.mEndOffset = textRange.mStartOffset + aLength;
textRange.mRangeType = aAttribute;
mClauses.AppendElement(textRange);
return NS_OK;
}
default:
return NS_ERROR_INVALID_ARG;
}
}
NS_IMETHODIMP
CompositionStringSynthesizer::SetCaret(uint32_t aOffset, uint32_t aLength)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
mCaret.mStartOffset = aOffset;
mCaret.mEndOffset = mCaret.mStartOffset + aLength;
mCaret.mRangeType = NS_TEXTRANGE_CARETPOSITION;
return NS_OK;
}
NS_IMETHODIMP
CompositionStringSynthesizer::DispatchEvent(bool* aDefaultPrevented)
{
NS_ENSURE_ARG_POINTER(aDefaultPrevented);
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
if (!mClauses.IsEmpty()) {
NS_ENSURE_TRUE(mClauses[mClauses.Length()-1].mEndOffset == mString.Length(),
NS_ERROR_ILLEGAL_VALUE);
}
if (mCaret.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
NS_ENSURE_TRUE(mCaret.mEndOffset <= mString.Length(),
NS_ERROR_ILLEGAL_VALUE);
mClauses.AppendElement(mCaret);
}
nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
textEvent.time = PR_IntervalNow();
textEvent.theText = mString;
textEvent.rangeCount = mClauses.Length();
textEvent.rangeArray = mClauses.Elements();
// XXX How should we set false for this on b2g?
textEvent.mFlags.mIsSynthesizedForTests = true;
nsEventStatus status = nsEventStatus_eIgnore;
nsresult rv = widget->DispatchEvent(&textEvent, status);
*aDefaultPrevented = (status == nsEventStatus_eConsumeNoDefault);
ClearInternal();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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_dom_compositionstringsynthesizer_h__
#define mozilla_dom_compositionstringsynthesizer_h__
#include "nsICompositionStringSynthesizer.h"
#include "nsGUIEvent.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
#include "mozilla/Attributes.h"
class nsIWidget;
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class CompositionStringSynthesizer MOZ_FINAL :
public nsICompositionStringSynthesizer
{
public:
CompositionStringSynthesizer(nsPIDOMWindow* aWindow);
~CompositionStringSynthesizer();
NS_DECL_ISUPPORTS
NS_DECL_NSICOMPOSITIONSTRINGSYNTHESIZER
private:
nsWeakPtr mWindow; // refers an instance of nsPIDOMWindow
nsString mString;
nsAutoTArray<nsTextRange, 10> mClauses;
nsTextRange mCaret;
nsIWidget* GetWidget();
void ClearInternal();
};
} // namespace dom
} // namespace mozilla
#endif // #ifndef mozilla_dom_compositionstringsynthesizer_h__

View File

@ -65,6 +65,7 @@ EXPORTS.mozilla.dom += [
CPP_SOURCES += [
'BarProps.cpp',
'CompositionStringSynthesizer.cpp',
'Crypto.cpp',
'DOMCursor.cpp',
'DOMError.cpp',

View File

@ -11,6 +11,7 @@
#include "nsIDOMEvent.h"
#include "nsDOMWindowUtils.h"
#include "nsQueryContentEventResult.h"
#include "CompositionStringSynthesizer.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "nsFocusManager.h"
@ -1816,82 +1817,21 @@ nsDOMWindowUtils::SendCompositionEvent(const nsAString& aType,
return NS_OK;
}
static void
AppendClause(int32_t aClauseLength, uint32_t aClauseAttr,
nsTArray<nsTextRange>* aRanges)
{
NS_PRECONDITION(aRanges, "aRange is null");
if (aClauseLength == 0) {
return;
}
nsTextRange range;
range.mStartOffset = aRanges->Length() == 0 ? 0 :
aRanges->ElementAt(aRanges->Length() - 1).mEndOffset + 1;
range.mEndOffset = range.mStartOffset + aClauseLength;
NS_ASSERTION(range.mStartOffset <= range.mEndOffset, "range is invalid");
NS_PRECONDITION(aClauseAttr == NS_TEXTRANGE_RAWINPUT ||
aClauseAttr == NS_TEXTRANGE_SELECTEDRAWTEXT ||
aClauseAttr == NS_TEXTRANGE_CONVERTEDTEXT ||
aClauseAttr == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT,
"aClauseAttr is invalid value");
range.mRangeType = aClauseAttr;
aRanges->AppendElement(range);
}
NS_IMETHODIMP
nsDOMWindowUtils::SendTextEvent(const nsAString& aCompositionString,
int32_t aFirstClauseLength,
uint32_t aFirstClauseAttr,
int32_t aSecondClauseLength,
uint32_t aSecondClauseAttr,
int32_t aThirdClauseLength,
uint32_t aThirdClauseAttr,
int32_t aCaretStart,
int32_t aCaretLength)
nsDOMWindowUtils::CreateCompositionStringSynthesizer(
nsICompositionStringSynthesizer** aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = nullptr;
if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
// get the widget to send the event to
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget) {
return NS_ERROR_FAILURE;
}
nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
InitEvent(textEvent);
nsAutoTArray<nsTextRange, 4> textRanges;
NS_ENSURE_TRUE(aFirstClauseLength >= 0, NS_ERROR_INVALID_ARG);
NS_ENSURE_TRUE(aSecondClauseLength >= 0, NS_ERROR_INVALID_ARG);
NS_ENSURE_TRUE(aThirdClauseLength >= 0, NS_ERROR_INVALID_ARG);
AppendClause(aFirstClauseLength, aFirstClauseAttr, &textRanges);
AppendClause(aSecondClauseLength, aSecondClauseAttr, &textRanges);
AppendClause(aThirdClauseLength, aThirdClauseAttr, &textRanges);
int32_t len = aFirstClauseLength + aSecondClauseLength + aThirdClauseLength;
NS_ENSURE_TRUE(len == 0 || uint32_t(len) == aCompositionString.Length(),
NS_ERROR_FAILURE);
if (aCaretStart >= 0) {
nsTextRange range;
range.mStartOffset = aCaretStart;
range.mEndOffset = range.mStartOffset + aCaretLength;
range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
textRanges.AppendElement(range);
}
textEvent.theText = aCompositionString;
textEvent.rangeCount = textRanges.Length();
textEvent.rangeArray = textRanges.Elements();
textEvent.mFlags.mIsSynthesizedForTests = true;
nsEventStatus status;
nsresult rv = widget->DispatchEvent(&textEvent, status);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
NS_ADDREF(*aResult = new CompositionStringSynthesizer(window));
return NS_OK;
}

View File

@ -7,6 +7,7 @@
XPIDL_SOURCES += [
'domstubs.idl',
'nsIBrowserDOMWindow.idl',
'nsICompositionStringSynthesizer.idl',
'nsIContentPermissionPrompt.idl',
'nsIContentPrefService.idl',
'nsIContentPrefService2.idl',

View File

@ -0,0 +1,53 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsISupports.idl"
/**
* Stores composition clauses information and caret information for synthesizing
* composition string.
*/
[scriptable, uuid(9a7d7851-8c0a-4061-9edc-60d6693f86c9)]
interface nsICompositionStringSynthesizer : nsISupports
{
/**
* Set composition string or committed string.
*/
void setString(in AString aString);
// NOTE: These values must be same to NS_TEXTRANGE_* in nsGUIEvent.h
const unsigned long ATTR_RAWINPUT = 0x02;
const unsigned long ATTR_SELECTEDRAWTEXT = 0x03;
const unsigned long ATTR_CONVERTEDTEXT = 0x04;
const unsigned long ATTR_SELECTEDCONVERTEDTEXT = 0x05;
/**
* Append a clause.
*
* TODO: Should be able to specify custom clause style.
*/
void appendClause(in unsigned long aLength,
in unsigned long aAttribute);
/**
* Set caret information.
*/
void setCaret(in unsigned long aOffset,
in unsigned long aLength);
/**
* Synthesize composition string with given information by dispatching
* a proper event.
*
* If clauses have never been set, this dispatches a commit event.
* If clauses are not filled all over the composition string, this throw an
* error.
*
* After dispatching event, this clears all the information about the
* composition string. So, you can reuse this instance.
*/
bool dispatchEvent();
};

View File

@ -41,8 +41,9 @@ interface nsIDOMClientRect;
interface nsIURI;
interface nsIDOMEventTarget;
interface nsIRunnable;
interface nsICompositionStringSynthesizer;
[scriptable, uuid(d18a8d69-7609-4165-ae20-af8aead36833)]
[scriptable, uuid(dd45c6ae-9d80-46ef-86d7-f2795a48a77b)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -787,44 +788,12 @@ interface nsIDOMWindowUtils : nsISupports {
in AString aLocale);
/**
* Synthesize a text event to the window.
* Creating synthesizer of composition string on the window.
*
* Cannot be accessed from unprivileged context (not content-accessible)
* Will throw a DOM security error if called without chrome privileges.
*
* Currently, this method doesn't support 4 or more clauses composition
* string.
*
* @param aCompositionString composition string
* @param a*ClauseLengh the length of nth clause, set 0 when you
* don't need second or third clause.
* @param a*ClauseAttr the attribute of nth clause, uese following
* const values.
* @param aCaretStart the caret position in the composition string,
* if you set negative value, this method don't
* set the caret position to the event.
* @param aCaretLength the caret length, if this is one or more,
* the caret will be wide caret, otherwise,
* it's collapsed.
* XXX nsEditor doesn't support wide caret yet.
*/
// NOTE: These values must be same to NS_TEXTRANGE_* in nsGUIEvent.h
const unsigned long COMPOSITION_ATTR_RAWINPUT = 0x02;
const unsigned long COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
const unsigned long COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
const unsigned long COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
void sendTextEvent(in AString aCompositionString,
in long aFirstClauseLength,
in unsigned long aFirstClauseAttr,
in long aSecondClauseLength,
in unsigned long aSecondClauseAttr,
in long aThirdClauseLength,
in unsigned long aThirdClauseAttr,
in long aCaretStart,
in long aCaretLength);
nsICompositionStringSynthesizer createCompositionStringSynthesizer();
/**
* Synthesize a query content event. Note that the result value returned here