michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "CompositionStringSynthesizer.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsView.h" michael@0: #include "mozilla/TextEvents.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: NS_IMPL_ISUPPORTS(CompositionStringSynthesizer, michael@0: nsICompositionStringSynthesizer) michael@0: michael@0: CompositionStringSynthesizer::CompositionStringSynthesizer( michael@0: nsPIDOMWindow* aWindow) michael@0: { michael@0: mWindow = do_GetWeakReference(aWindow); michael@0: mClauses = new TextRangeArray(); michael@0: ClearInternal(); michael@0: } michael@0: michael@0: CompositionStringSynthesizer::~CompositionStringSynthesizer() michael@0: { michael@0: } michael@0: michael@0: void michael@0: CompositionStringSynthesizer::ClearInternal() michael@0: { michael@0: mString.Truncate(); michael@0: mClauses->Clear(); michael@0: mCaret.mRangeType = 0; michael@0: } michael@0: michael@0: nsIWidget* michael@0: CompositionStringSynthesizer::GetWidget() michael@0: { michael@0: nsCOMPtr window = do_QueryReferent(mWindow); michael@0: if (!window) { michael@0: return nullptr; michael@0: } michael@0: nsIDocShell *docShell = window->GetDocShell(); michael@0: if (!docShell) { michael@0: return nullptr; michael@0: } michael@0: nsCOMPtr presShell = docShell->GetPresShell(); michael@0: if (!presShell) { michael@0: return nullptr; michael@0: } michael@0: nsIFrame* frame = presShell->GetRootFrame(); michael@0: if (!frame) { michael@0: return nullptr; michael@0: } michael@0: return frame->GetView()->GetNearestWidget(nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CompositionStringSynthesizer::SetString(const nsAString& aString) michael@0: { michael@0: nsCOMPtr widget = GetWidget(); michael@0: NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: mString = aString; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CompositionStringSynthesizer::AppendClause(uint32_t aLength, michael@0: uint32_t aAttribute) michael@0: { michael@0: nsCOMPtr widget = GetWidget(); michael@0: NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: switch (aAttribute) { michael@0: case ATTR_RAWINPUT: michael@0: case ATTR_SELECTEDRAWTEXT: michael@0: case ATTR_CONVERTEDTEXT: michael@0: case ATTR_SELECTEDCONVERTEDTEXT: { michael@0: TextRange textRange; michael@0: textRange.mStartOffset = michael@0: mClauses->IsEmpty() ? 0 : mClauses->LastElement().mEndOffset; michael@0: textRange.mEndOffset = textRange.mStartOffset + aLength; michael@0: textRange.mRangeType = aAttribute; michael@0: mClauses->AppendElement(textRange); michael@0: return NS_OK; michael@0: } michael@0: default: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CompositionStringSynthesizer::SetCaret(uint32_t aOffset, uint32_t aLength) michael@0: { michael@0: nsCOMPtr widget = GetWidget(); michael@0: NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: mCaret.mStartOffset = aOffset; michael@0: mCaret.mEndOffset = mCaret.mStartOffset + aLength; michael@0: mCaret.mRangeType = NS_TEXTRANGE_CARETPOSITION; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CompositionStringSynthesizer::DispatchEvent(bool* aDefaultPrevented) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDefaultPrevented); michael@0: nsCOMPtr widget = GetWidget(); michael@0: NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: michael@0: if (!mClauses->IsEmpty() && michael@0: mClauses->LastElement().mEndOffset != mString.Length()) { michael@0: NS_WARNING("Sum of length of the all clauses must be same as the string " michael@0: "length"); michael@0: ClearInternal(); michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: if (mCaret.mRangeType == NS_TEXTRANGE_CARETPOSITION) { michael@0: if (mCaret.mEndOffset > mString.Length()) { michael@0: NS_WARNING("Caret position is out of the composition string"); michael@0: ClearInternal(); michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: mClauses->AppendElement(mCaret); michael@0: } michael@0: michael@0: WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); michael@0: textEvent.time = PR_IntervalNow(); michael@0: textEvent.theText = mString; michael@0: if (!mClauses->IsEmpty()) { michael@0: textEvent.mRanges = mClauses; michael@0: } michael@0: michael@0: // XXX How should we set false for this on b2g? michael@0: textEvent.mFlags.mIsSynthesizedForTests = true; michael@0: michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: nsresult rv = widget->DispatchEvent(&textEvent, status); michael@0: *aDefaultPrevented = (status == nsEventStatus_eConsumeNoDefault); michael@0: michael@0: ClearInternal(); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla